49044f6fbea91283a07f829529331e06e276272a
[reactos.git] / rosapps / mc / src / file.c
1 /* {{{ Copyright */
2
3 /* File managing. Important notes on this file:
4
5 About the use of dialogs in this file:
6 If you want to add a new dialog box (or call a routine that pops
7 up a dialog box), you have to provide a wrapper for background
8 operations (ie, background operations have to up-call to the parent
9 process).
10
11 For example, instead of using the message() routine, in this
12 file, you should use one of the stubs that call message with the
13 proper number of arguments (ie, message_1s, message_2s and so on).
14
15 Actually, that is a rule that should be followed by any routines
16 that may be called from this module.
17
18 */
19
20 /* File managing
21 Copyright (C) 1994, 1995, 1996 The Free Software Foundation
22
23 Written by: 1994, 1995 Janne Kukonlehto
24 1994, 1995 Fred Leeflang
25 1994, 1995, 1996 Miguel de Icaza
26 1995, 1996 Jakub Jelinek
27 1997 Norbert Warmuth
28 1998 Pavel Machek
29
30 The copy code was based in GNU's cp, and was written by:
31 Torbjorn Granlund, David MacKenzie, and Jim Meyering.
32
33 The move code was based in GNU's mv, and was written by:
34 Mike Parker and David MacKenzie.
35
36 Janne Kukonlehto added much error recovery to them for being used
37 in an interactive program.
38
39 This program is free software; you can redistribute it and/or modify
40 it under the terms of the GNU General Public License as published by
41 the Free Software Foundation; either version 2 of the License, or
42 (at your option) any later version.
43
44 This program is distributed in the hope that it will be useful,
45 but WITHOUT ANY WARRANTY; without even the implied warranty of
46 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47 GNU General Public License for more details.
48
49 You should have received a copy of the GNU General Public License
50 along with this program; if not, write to the Free Software
51 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
52
53 /* }}} */
54
55 /* {{{ Include files */
56
57 #include <config.h>
58 /* Hack: the vfs code should not rely on this */
59 #define WITH_FULL_PATHS 1
60
61 #include <sys/types.h>
62 #include <dirent.h>
63 #include <stdio.h>
64 #ifdef OS2_NT
65 # include <io.h>
66 #endif
67
68 #include <errno.h>
69 #include "tty.h"
70 #include <ctype.h>
71 #include <malloc.h>
72 #include <string.h>
73 #ifdef HAVE_UNISTD_H
74 # include <unistd.h>
75 #endif
76 #include <sys/stat.h>
77 #include <sys/param.h>
78 #include <fcntl.h>
79 #ifdef SCO_FLAVOR
80 # include <sys/timeb.h> /* alex: for struct timeb, used in time.h */
81 #endif /* SCO_FLAVOR */
82 #if defined(_MSC_VER)
83 #include <sys/time.h___>
84 #else
85 #include <time.h>
86 #endif
87 #include <utime.h>
88 #include "mad.h"
89 #include "regex.h"
90 #include "util.h"
91 #include "dialog.h"
92 #include "global.h"
93 /* Needed by query_replace */
94 #include "color.h"
95 #include "win.h"
96 #include "dlg.h"
97 #include "widget.h"
98 #define WANT_WIDGETS
99 #include "main.h" /* WANT_WIDGETS-> we get the the_hint def */
100 #include "file.h"
101 #include "layout.h"
102 #include "widget.h"
103 #include "wtools.h"
104 #include "background.h"
105
106 /* Needed for current_panel, other_panel and WTree */
107 #include "dir.h"
108 #include "panel.h"
109 #include "tree.h"
110 #include "key.h"
111 #include "../vfs/vfs.h"
112
113 #include "x.h"
114
115 /* }}} */
116
117 #if USE_VFS && USE_NETCODE
118 extern
119 #else
120 static
121 #endif
122
123 int do_reget;
124
125 /* rcsid [] = "$Id: file.c,v 1.2 2003/06/27 21:10:35 gvg Exp $" */
126 int verbose = 1;
127
128 /* Recursive operation on subdirectories */
129 int dive_into_subdirs = 0;
130
131 /* When moving directories cross filesystem boundaries delete the successfull
132 copied files when all files below the directory and its subdirectories
133 were processed.
134 If erase_at_end is zero files will be deleted immediately after their
135 successful copy (Note: this behaviour is not tested and at the moment
136 it can't be changed at runtime) */
137 int erase_at_end = 1;
138
139 /* Preserve the original files' owner, group, permissions, and
140 timestamps (owner, group only as root). */
141 int preserve;
142
143 /* The value of the "preserve Attributes" checkbox in the copy file dialog.
144 We can't use the value of "preserve" because it can change in order to
145 preserve file attributs when moving files across filesystem boundaries
146 (we want to keep the value of the checkbox between copy operations). */
147 int op_preserve = 1;
148
149 /* If running as root, preserve the original uid/gid
150 (we don't want to try chwon for non root)
151 preserve_uidgid = preserve && uid == 0 */
152 int preserve_uidgid = 0;
153
154 /* The bits to preserve in created files' modes on file copy */
155 int umask_kill = 0777777;
156
157 /* If on, it gets a little scrict with dangerous operations */
158 int know_not_what_am_i_doing = 0;
159
160 int stable_symlinks = 0;
161
162 /* The next two are not static, since they are used on background.c */
163 /* Controls appending to files, shared with filequery.c */
164 int do_append = 0;
165
166 /* With ETA on we have extra screen space */
167 int eta_extra = 0;
168
169 /* result from the recursive query */
170 int recursive_result;
171
172 /* The estimated time of arrival in seconds */
173 double eta_secs;
174
175 /* Used to save the hint line */
176 static int last_hint_line;
177
178 /* mapping operations into names */
179 char *operation_names [] = { "Copy", "Move", "Delete" };
180
181 /* This is a hard link cache */
182 struct link {
183 struct link *next;
184 vfs *vfs;
185 dev_t dev;
186 ino_t ino;
187 short linkcount;
188 umode_t st_mode;
189 char name[1];
190 };
191
192 /* the hard link cache */
193 struct link *linklist = NULL;
194
195 /* the files-to-be-erased list */
196 struct link *erase_list;
197
198 /* In copy_dir_dir we use two additional single linked lists: The first -
199 variable name `parent_dirs' - holds information about already copied
200 directories and is used to detect cyclic symbolic links.
201 The second (`dest_dirs' below) holds information about just created
202 target directories and is used to detect when an directory is copied
203 into itself (we don't want to copy infinitly).
204 Both lists don't use the linkcount and name structure members of struct
205 link. */
206 struct link *dest_dirs = 0;
207
208 struct re_pattern_buffer rx;
209 struct re_registers regs;
210 static char *dest_mask = NULL;
211
212 /* To symlinks the difference between `follow Links' checked and not
213 checked is the stat call used (mc_stat resp. mc_lstat) */
214 int (*xstat)(char *, struct stat *) = mc_lstat;
215
216 static int op_follow_links = 0;
217
218 /* File operate window sizes */
219 #define WX 62
220 #define WY 10
221 #define BY 10
222 #define WX_ETA_EXTRA 12
223
224 #define FCOPY_GAUGE_X 14
225 #define FCOPY_LABEL_X 5
226
227 /* Used for button result values */
228 enum {
229 REPLACE_YES = B_USER,
230 REPLACE_NO,
231 REPLACE_APPEND,
232 REPLACE_ALWAYS,
233 REPLACE_UPDATE,
234 REPLACE_NEVER,
235 REPLACE_ABORT,
236 REPLACE_SIZE,
237 REPLACE_REGET
238 };
239
240 enum {
241 RECURSIVE_YES,
242 RECURSIVE_NO,
243 RECURSIVE_ALWAYS,
244 RECURSIVE_NEVER,
245 RECURSIVE_ABORT
246 };
247
248 /* Pointer to the operate dialog */
249 static Dlg_head *op_dlg;
250 int showing_eta;
251 int showing_bps;
252 unsigned long bps = 0, bps_time = 0;
253
254 static char *op_names [] = { N_(" Copy "), N_(" Move "), N_(" Delete ") };
255 static int selected_button;
256 static int last_percentage [3];
257
258 /* Replace dialog: color set, descriptor and filename */
259 static int replace_colors [4];
260 static Dlg_head *replace_dlg;
261 static char *replace_filename;
262 static int replace_result;
263
264 static struct stat *s_stat, *d_stat;
265
266 static int recursive_erase (char *s);
267 static int erase_file (char *s);
268
269 /* Describe the components in the panel operations window */
270 static WLabel *FileLabel [2];
271 static WLabel *FileString [2];
272 static WLabel *ProgressLabel [3];
273 static WGauge *ProgressGauge [3];
274 static WLabel *eta_label;
275 static WLabel *bps_label;
276 static WLabel *stalled_label;
277
278 /* }}} */
279
280 /* {{{ File progress display routines */
281
282 #ifndef HAVE_X
283 static int
284 check_buttons (void)
285 {
286 int c;
287 Gpm_Event event;
288
289 c = get_event (&event, 0, 0);
290 if (c == EV_NONE)
291 return FILE_CONT;
292 dlg_process_event (op_dlg, c, &event);
293 switch (op_dlg->ret_value) {
294 case FILE_SKIP:
295 return FILE_SKIP;
296 break;
297 case B_CANCEL:
298 case FILE_ABORT:
299 return FILE_ABORT;
300 break;
301 default:
302 return FILE_CONT;
303 }
304 }
305 #else
306
307 #ifdef HAVE_TK
308 static int
309 check_buttons (void)
310 {
311 tk_dispatch_all ();
312 if (op_dlg->running)
313 return FILE_CONT;
314 }
315 #endif /* HAVE_TK */
316
317 #ifdef HAVE_XVIEW
318 static int
319 check_buttons (void)
320 {
321 xv_dispatch_something ();
322 if (op_dlg->running)
323 return FILE_CONT;
324 }
325 #endif /* HAVE_XVIEW */
326
327 #ifdef HAVE_GNOME
328 #include <gtk/gtk.h>
329 static int
330 check_buttons (void)
331 {
332 x_flush_events ();
333
334 if (op_dlg->running)
335 return FILE_CONT;
336
337 if (op_dlg->ret_value == B_CANCEL)
338 return FILE_ABORT;
339 else
340 return op_dlg->ret_value;
341 }
342 #endif /* HAVE_GNOME */
343
344 #endif /* HAVE_X */
345
346 static int
347 op_win_callback (struct Dlg_head *h, int id, int msg)
348 {
349 switch (msg){
350 #ifndef HAVE_X
351 case DLG_DRAW:
352 attrset (COLOR_NORMAL);
353 dlg_erase (h);
354 draw_box (h, 1, 2, h->lines-2, h->cols-4);
355 return 1;
356 #endif
357 }
358 return 0;
359 }
360
361 void
362 create_op_win (int op, int with_eta)
363 {
364 int i, x_size;
365 int minus = verbose ? 0 : 3;
366 int eta_offset = with_eta ? (WX_ETA_EXTRA) / 2 : 0;
367
368 #ifdef HAVE_XVIEW
369 char *sixty = " ";
370 char *fifteen = " ";
371 #else
372 char *sixty = "";
373 char *fifteen = "";
374 #endif
375 replace_result = 0;
376 recursive_result = 0;
377 showing_eta = with_eta;
378 showing_bps = with_eta;
379 eta_extra = with_eta ? WX_ETA_EXTRA : 0;
380 x_size = (WX + 4) + eta_extra;
381
382 op_dlg = create_dlg (0, 0, WY-minus+4, x_size, dialog_colors,
383 op_win_callback, "", "opwin", DLG_CENTER);
384
385 #ifndef HAVE_X
386 last_hint_line = the_hint->widget.y;
387 if ((op_dlg->y + op_dlg->lines) > last_hint_line)
388 the_hint->widget.y = op_dlg->y + op_dlg->lines+1;
389 #endif
390
391 x_set_dialog_title (op_dlg, "");
392
393 tk_new_frame (op_dlg, "b.");
394 add_widgetl (op_dlg, button_new (BY-minus, WX - 19 + eta_offset, FILE_ABORT,
395 NORMAL_BUTTON, _("&Abort"), 0, 0, "abort"),
396 XV_WLAY_RIGHTOF);
397 add_widgetl (op_dlg, button_new (BY-minus, 14 + eta_offset, FILE_SKIP,
398 NORMAL_BUTTON, _("&Skip"), 0, 0, "skip"),
399 XV_WLAY_CENTERROW);
400
401 tk_new_frame (op_dlg, "2.");
402 add_widgetl (op_dlg, ProgressGauge [2] = gauge_new (7, FCOPY_GAUGE_X, 0, 100, 0, "g-1"),
403 XV_WLAY_RIGHTOF);
404 add_widgetl (op_dlg, ProgressLabel [2] = label_new (7, FCOPY_LABEL_X, fifteen, "l-1"),
405 XV_WLAY_NEXTROW);
406 add_widgetl (op_dlg, bps_label = label_new (7, WX, "", "bps-label"), XV_WLAY_NEXTROW);
407
408 tk_new_frame (op_dlg, "1.");
409 add_widgetl (op_dlg, ProgressGauge [1] = gauge_new (8, FCOPY_GAUGE_X, 0, 100, 0, "g-2"),
410 XV_WLAY_RIGHTOF);
411 add_widgetl (op_dlg, ProgressLabel [1] = label_new (8, FCOPY_LABEL_X, fifteen, "l-2"),
412 XV_WLAY_RIGHTOF);
413 add_widgetl (op_dlg, stalled_label = label_new (8, WX, "", "stalled"), XV_WLAY_NEXTROW);
414
415 tk_new_frame (op_dlg, "0.");
416 add_widgetl (op_dlg, ProgressGauge [0] = gauge_new (6, FCOPY_GAUGE_X, 0, 100, 0, "g-3"),
417 XV_WLAY_RIGHTOF);
418 add_widgetl (op_dlg, ProgressLabel [0] = label_new (6, FCOPY_LABEL_X, fifteen, "l-3"),
419 XV_WLAY_RIGHTOF);
420 add_widgetl (op_dlg, eta_label = label_new (6, WX, "", "eta_label"), XV_WLAY_NEXTROW);
421
422 tk_new_frame (op_dlg, "f1.");
423 add_widgetl (op_dlg, FileString [1] = label_new (4, FCOPY_GAUGE_X, sixty, "fs-l-1"),
424 XV_WLAY_RIGHTOF);
425 add_widgetl (op_dlg, FileLabel [1] = label_new (4, FCOPY_LABEL_X, fifteen, "fs-l-2"),
426 XV_WLAY_NEXTROW);
427 tk_new_frame (op_dlg, "f0.");
428 add_widgetl (op_dlg, FileString [0] = label_new (3, FCOPY_GAUGE_X, sixty, "fs-x-1"),
429 XV_WLAY_RIGHTOF);
430 add_widgetl (op_dlg, FileLabel [0] = label_new (3, FCOPY_LABEL_X, fifteen, "fs-x-2"),
431 XV_WLAY_NEXTROW);
432
433 /* We will manage the dialog without any help, that's why
434 we have to call init_dlg */
435 init_dlg (op_dlg);
436 op_dlg->running = 1;
437 selected_button = FILE_SKIP;
438 for (i = 0; i < 3; i++)
439 last_percentage [i] = -99;
440 }
441
442 void
443 destroy_op_win (void)
444 {
445 #ifdef HAVE_XVIEW
446 xtoolkit_kill_dialog (op_dlg);
447 #endif
448 dlg_run_done (op_dlg);
449 destroy_dlg (op_dlg);
450 #ifndef HAVE_X
451 the_hint->widget.y = last_hint_line;
452 #endif
453 }
454
455 static int
456 show_no_bar (int n)
457 {
458 if (n >= 0) {
459 label_set_text (ProgressLabel [n], "");
460 gauge_show (ProgressGauge [n], 0);
461 }
462 return check_buttons ();
463 }
464
465 #ifndef HAVE_X
466 #define truncFileString(s) name_trunc (s, eta_extra + 47)
467 #else
468 #define truncFileString(s) s
469 #endif
470
471 static int
472 show_source (char *s)
473 {
474 if (s != NULL){
475
476 #ifdef WITH_FULL_PATHS
477 int i = strlen (cpanel->cwd);
478
479 /* We remove the full path we have added before */
480 if (!strncmp (s, cpanel->cwd, i)){
481 if (s[i] == PATH_SEP)
482 s += i + 1;
483 }
484 #endif /* WITH_FULL_PATHS */
485
486 label_set_text (FileLabel [0], _("Source"));
487 label_set_text (FileString [0], truncFileString (s));
488 return check_buttons ();
489 } else {
490 label_set_text (FileLabel [0], "");
491 label_set_text (FileString [0], "");
492 return check_buttons ();
493 }
494 }
495
496 static int
497 show_target (char *s)
498 {
499 if (s != NULL){
500 label_set_text (FileLabel [1], _("Target"));
501 label_set_text (FileString [1], truncFileString (s));
502 return check_buttons ();
503 } else {
504 label_set_text (FileLabel [1], "");
505 label_set_text (FileString [1], "");
506 return check_buttons ();
507 }
508 }
509
510 static int
511 show_deleting (char *s)
512 {
513 label_set_text (FileLabel [0], _("Deleting"));
514 label_set_text (FileString [0], truncFileString (s));
515 return check_buttons ();
516 }
517
518 static int
519 show_bar (int n, long done, long total)
520 {
521 gauge_set_value (ProgressGauge [n], (int) total, (int) done);
522 gauge_show (ProgressGauge [n], 1);
523 return check_buttons ();
524 }
525
526 static void
527 file_eta_show ()
528 {
529 int eta_hours, eta_mins, eta_s;
530 char eta_buffer [30];
531
532 if (!showing_eta)
533 return;
534
535 eta_hours = eta_secs / (60 * 60);
536 eta_mins = (eta_secs - (eta_hours * 60 * 60)) / 60;
537 eta_s = eta_secs - ((eta_hours * 60 * 60) + eta_mins * 60 );
538 sprintf (eta_buffer, "ETA %d:%02d.%02d", eta_hours, eta_mins, eta_s);
539 label_set_text (eta_label, eta_buffer);
540 }
541
542 static void
543 file_bps_show ()
544 {
545 char bps_buffer [30];
546
547 if (!showing_bps)
548 return;
549
550 if (bps > 1024){
551 if (bps > 1024*1024){
552 sprintf (bps_buffer, "%.2f MBS", bps / (1024*1024.0));
553 } else
554 sprintf (bps_buffer, "%.2f KBS", bps / 1024.0);
555 } else
556 sprintf (bps_buffer, "%ld BPS", bps);
557 label_set_text (bps_label, bps_buffer);
558 }
559
560 static int
561 show_file_progress (long done, long total)
562 {
563 if (!verbose)
564 return check_buttons ();
565 if (total > 0){
566 label_set_text (ProgressLabel [0], _("File"));
567 file_eta_show ();
568 file_bps_show ();
569 return show_bar (0, done, total);
570 } else
571 return show_no_bar (0);
572 }
573
574 static int
575 show_count_progress (long done, long total)
576 {
577 if (!verbose)
578 return check_buttons ();
579 if (total > 0){
580 label_set_text (ProgressLabel [1], _("Count"));
581 return show_bar (1, done, total);
582 } else
583 return show_no_bar (1);
584 }
585
586 static int
587 show_bytes_progress (long done, long total)
588 {
589 if (!verbose)
590 return check_buttons ();
591 if (total > 0){
592 label_set_text (ProgressLabel [2], _("Bytes"));
593 return show_bar (2, done, total);
594 } else
595 return show_no_bar (2);
596 }
597
598 /* }}} */
599
600
601 /* {{{ Copy routines */
602
603 enum CaseConvs { NO_CONV=0, UP_CHAR=1, LOW_CHAR=2, UP_SECT=4, LOW_SECT=8 };
604
605 int
606 convert_case (int c, enum CaseConvs *conversion)
607 {
608 if (*conversion & UP_CHAR){
609 *conversion &= ~UP_CHAR;
610 return toupper (c);
611 } else if (*conversion & LOW_CHAR){
612 *conversion &= ~LOW_CHAR;
613 return tolower (c);
614 } else if (*conversion & UP_SECT){
615 return toupper (c);
616 } else if (*conversion & LOW_SECT){
617 return tolower (c);
618 } else
619 return c;
620 }
621
622 static int transform_error = 0;
623 static char *
624 do_transform_source (char *source)
625 {
626 int j, k, l, len;
627 char *fnsource = x_basename (source);
628 int next_reg;
629 enum CaseConvs case_conv = NO_CONV;
630 static char fntarget [MC_MAXPATHLEN];
631
632 len = strlen (fnsource);
633 j = re_match (&rx, fnsource, len, 0, &regs);
634 if (j != len) {
635 transform_error = FILE_SKIP;
636 return NULL;
637 }
638 for (next_reg = 1, j = 0, k = 0; j < strlen (dest_mask); j++) {
639 switch (dest_mask [j]) {
640 case '\\':
641 j++;
642 if (! isdigit (dest_mask [j])){
643 /* Backslash followed by non-digit */
644 switch (dest_mask [j]){
645 case 'U':
646 case_conv |= UP_SECT;
647 case_conv &= ~LOW_SECT;
648 break;
649 case 'u':
650 case_conv |= UP_CHAR;
651 break;
652 case 'L':
653 case_conv |= LOW_SECT;
654 case_conv &= ~UP_SECT;
655 break;
656 case 'l':
657 case_conv |= LOW_CHAR;
658 break;
659 case 'E':
660 case_conv = NO_CONV;
661 break;
662 default:
663 /* Backslash as quote mark */
664 fntarget [k++] = convert_case (dest_mask [j], &case_conv);
665 }
666 break;
667 } else {
668 /* Backslash followed by digit */
669 next_reg = dest_mask [j] - '0';
670 /* Fall through */
671 }
672
673 case '*':
674 if (next_reg < 0 || next_reg >= RE_NREGS
675 || regs.start [next_reg] < 0) {
676 message_1s (1, MSG_ERROR, _(" Invalid target mask "));
677 transform_error = FILE_ABORT;
678 return NULL;
679 }
680 for (l = regs.start [next_reg]; l < regs.end [next_reg]; l++)
681 fntarget [k++] = convert_case (fnsource [l], &case_conv);
682 next_reg ++;
683 break;
684
685 default:
686 fntarget [k++] = convert_case (dest_mask [j], &case_conv);
687 break;
688 }
689 }
690 fntarget [k] = 0;
691 return fntarget;
692 }
693
694 static char *
695 transform_source (char *source)
696 {
697 char *s = strdup (source);
698 char *q;
699
700 /* We remove \n from the filename since regex routines would use \n as an anchor */
701 /* this is just to be allowed to maniupulate file names with \n on it */
702 for (q = s; *q; q++){
703 if (*q == '\n')
704 *q = ' ';
705 }
706 q = do_transform_source (s);
707 free (s);
708 return q;
709 }
710
711 void
712 free_linklist (struct link **linklist)
713 {
714 struct link *lp, *lp2;
715
716 for (lp = *linklist; lp != NULL; lp = lp2){
717 lp2 = lp -> next;
718 free (lp);
719 }
720 *linklist = NULL;
721 }
722
723 #ifdef USE_VFS
724 int
725 is_in_linklist (struct link *lp, char *path, struct stat *sb)
726 {
727 ino_t ino = sb->st_ino;
728 dev_t dev = sb->st_dev;
729 vfs *vfs = vfs_type (path);
730
731 while (lp) {
732 if (lp->vfs == vfs && lp->ino == ino && lp->dev == dev )
733 return 1;
734 lp = lp->next;
735 }
736 return 0;
737 }
738 #else
739 int
740 is_in_linklist (struct link *lp, char *path, struct stat *sb)
741 {
742 ino_t ino = sb->st_ino;
743 dev_t dev = sb->st_dev;
744
745 while (lp) {
746 if (lp->ino == ino && lp->dev == dev )
747 return 1;
748 lp = lp->next;
749 }
750 return 0;
751 }
752 #endif
753
754 /* Returns 0 if the inode wasn't found in the cache and 1 if it was found
755 and a hardlink was succesfully made */
756 int
757 check_hardlinks (char *src_name, char *dst_name, struct stat *pstat)
758 {
759 struct link *lp;
760 vfs *my_vfs = vfs_type (src_name);
761 ino_t ino = pstat->st_ino;
762 dev_t dev = pstat->st_dev;
763 struct stat link_stat;
764 char *p;
765
766 if (vfs_file_is_ftp (src_name))
767 return 0;
768 for (lp = linklist; lp != NULL; lp = lp -> next)
769 if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev){
770 if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino &&
771 link_stat.st_dev == dev && vfs_type (lp->name) == my_vfs){
772 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
773 was copied to */
774 if (vfs_type (dst_name) == vfs_type (p)){
775 if (!mc_stat (p, &link_stat)){
776 if (!mc_link (p, dst_name))
777 return 1;
778 }
779 }
780 }
781 /* FIXME: Announce we couldn't make the hardlink */
782 return 0;
783 }
784 lp = (struct link *) xmalloc (sizeof (struct link) + strlen (src_name)
785 + strlen (dst_name) + 1, "Hardlink cache");
786 if (lp){
787 lp->vfs = my_vfs;
788 lp->ino = ino;
789 lp->dev = dev;
790 strcpy (lp->name, src_name);
791 p = strchr (lp->name, 0) + 1;
792 strcpy (p, dst_name);
793 lp->next = linklist;
794 linklist = lp;
795 }
796 return 0;
797 }
798
799 /* Duplicate the contents of the symbolic link src_path in dst_path.
800 Try to make a stable symlink if the option "stable symlink" was
801 set in the file mask dialog.
802 If dst_path is an existing symlink it will be deleted silently
803 (upper levels take already care of existing files at dst_path).
804 */
805 static int
806 make_symlink (char *src_path, char *dst_path)
807 {
808 char link_target[MC_MAXPATHLEN];
809 int len;
810 int return_status;
811 struct stat sb;
812 int dst_is_symlink;
813
814 if (mc_lstat (dst_path, &sb) == 0 && S_ISLNK (sb.st_mode))
815 dst_is_symlink = 1;
816 else
817 dst_is_symlink = 0;
818
819 retry_src_readlink:
820 len = mc_readlink (src_path, link_target, MC_MAXPATHLEN);
821 if (len < 0) {
822 return_status = file_error
823 (_(" Cannot read source link \"%s\" \n %s "), src_path);
824 if (return_status == FILE_RETRY)
825 goto retry_src_readlink;
826 return return_status;
827 }
828 link_target[len] = 0;
829
830 if (stable_symlinks && (!vfs_file_is_local (src_path) ||
831 !vfs_file_is_local (dst_path))) {
832 message_1s (1, MSG_ERROR, _(" Cannot make stable symlinks across "
833 "non-local filesystems: \n\n"
834 " Option Stable Symlinks will be disabled "));
835 stable_symlinks = 0;
836 }
837
838 if (stable_symlinks && *link_target != PATH_SEP) {
839 char *p, *q, *r, *s;
840
841 p = strdup (src_path);
842 r = strrchr (p, PATH_SEP);
843 if (r) {
844 r[1] = 0;
845 if (*dst_path == PATH_SEP)
846 q = strdup (dst_path);
847 else
848 q = copy_strings (p, dst_path, 0);
849 r = strrchr (q, PATH_SEP);
850 if (r) {
851 r[1] = 0;
852 s = copy_strings (p, link_target, NULL);
853 strcpy (link_target, s);
854 free (s);
855 s = diff_two_paths (q, link_target);
856 if (s) {
857 strcpy (link_target, s);
858 free (s);
859 }
860 }
861 free (q);
862 }
863 free (p);
864 }
865 retry_dst_symlink:
866 if (mc_symlink (link_target, dst_path) == 0)
867 /* Success */
868 return FILE_CONT;
869 /*
870 * if dst_exists, it is obvious that this had failed.
871 * We can delete the old symlink and try again...
872 */
873 if (dst_is_symlink) {
874 if (!mc_unlink (dst_path))
875 if (mc_symlink (link_target, dst_path) == 0)
876 /* Success */
877 return FILE_CONT;
878 }
879 return_status = file_error
880 (_(" Cannot create target symlink \"%s\" \n %s "), dst_path);
881 if (return_status == FILE_RETRY)
882 goto retry_dst_symlink;
883 return return_status;
884 }
885
886
887 int
888 copy_file_file (char *src_path, char *dst_path, int ask_overwrite)
889 {
890 #ifndef OS2_NT
891 uid_t src_uid;
892 gid_t src_gid;
893 #endif
894 char *buf = 0;
895 int buf_size = 8*1024;
896 int dest_desc = 0;
897 int source_desc;
898 int n_read;
899 int n_written;
900 int src_mode; /* The mode of the source file */
901 struct stat sb, sb2;
902 struct utimbuf utb;
903 int dst_exists = 0;
904 long n_read_total = 0;
905 long file_size;
906 int return_status, temp_status;
907 int do_remote_copy = 0;
908 int appending = 0;
909 /* bitmask used to remember which resourses we should release on return
910 A single goto label is much easier to handle than a bunch of gotos ;-). */
911 unsigned resources = 0;
912
913 return_status = FILE_RETRY;
914
915 if (show_source (src_path) == FILE_ABORT
916 || show_target (dst_path) == FILE_ABORT)
917 return FILE_ABORT;
918 mc_refresh ();
919
920 retry_dst_stat:
921 if (mc_stat (dst_path, &sb2) == 0){
922 if (S_ISDIR (sb2.st_mode)){
923 return_status = file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
924 dst_path);
925 if (return_status == FILE_RETRY)
926 goto retry_dst_stat;
927 return return_status;
928 }
929 dst_exists = 1;
930 }
931
932 retry_src_xstat:
933 if ((*xstat)(src_path, &sb)){
934 return_status = file_error (_(" Cannot stat source file \"%s\" \n %s "),
935 src_path);
936 if (return_status == FILE_RETRY)
937 goto retry_src_xstat;
938 return return_status;
939 }
940
941 if (dst_exists){
942 /* .ado: For OS/2 or NT: no st_ino exists, it is better to just try to
943 * overwrite the target file
944 */
945 #ifndef OS2_NT
946 /* Destination already exists */
947 if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino){
948 message_3s (1, MSG_ERROR, _(" `%s' and `%s' are the same file. "),
949 src_path, dst_path);
950 do_refresh ();
951 return FILE_SKIP;
952 }
953 #endif
954
955 /* Should we replace destination? */
956 if (ask_overwrite) {
957 if (vfs_file_is_ftp (src_path))
958 do_reget = -1;
959 else
960 do_reget = 0;
961
962 return_status = query_replace (dst_path, &sb, &sb2);
963 if (return_status != FILE_CONT)
964 return return_status;
965 }
966 }
967
968 if (!do_append) {
969 /* .ado: OS2 and NT don't have hardlinks */
970 #ifndef OS2_NT
971 /* Check the hardlinks */
972 if (!op_follow_links && sb.st_nlink > 1 &&
973 check_hardlinks (src_path, dst_path, &sb) == 1) {
974 /* We have made a hardlink - no more processing is necessary */
975 return return_status;
976 }
977
978 if (S_ISLNK (sb.st_mode))
979 return make_symlink (src_path, dst_path);
980
981 #endif /* !OS_NT */
982
983 if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) || S_ISFIFO (sb.st_mode)
984 || S_ISSOCK (sb.st_mode)){
985 retry_mknod:
986 if (mc_mknod (dst_path, sb.st_mode & umask_kill, sb.st_rdev) < 0){
987 return_status = file_error
988 (_(" Cannot create special file \"%s\" \n %s "), dst_path);
989 if (return_status == FILE_RETRY)
990 goto retry_mknod;
991 return return_status;
992 }
993 /* Success */
994
995 #ifndef OS2_NT
996 retry_mknod_uidgid:
997 if (preserve_uidgid && mc_chown (dst_path, sb.st_uid, sb.st_gid)){
998 temp_status = file_error
999 (_(" Cannot chown target file \"%s\" \n %s "), dst_path);
1000 if (temp_status == FILE_RETRY)
1001 goto retry_mknod_uidgid;
1002 return temp_status;
1003 }
1004 #endif
1005 #ifndef __os2__
1006 retry_mknod_chmod:
1007 if (preserve && mc_chmod (dst_path, sb.st_mode & umask_kill) < 0){
1008 temp_status = file_error (_(" Cannnot chmod target file \"%s\" \n %s "), dst_path);
1009 if (temp_status == FILE_RETRY)
1010 goto retry_mknod_chmod;
1011 return temp_status;
1012 }
1013 #endif
1014 return FILE_CONT;
1015 }
1016 }
1017
1018 if (!do_append && !vfs_file_is_local (src_path) && vfs_file_is_local (dst_path)){
1019 mc_setctl (src_path, MCCTL_SETREMOTECOPY, dst_path);
1020 }
1021 retry_src_open:
1022 if ((source_desc = mc_open (src_path, O_RDONLY)) < 0){
1023 return_status = file_error
1024 (_(" Cannot open source file \"%s\" \n %s "), src_path);
1025 if (return_status == FILE_RETRY)
1026 goto retry_src_open;
1027 do_append = 0;
1028 return return_status;
1029 }
1030
1031 resources |= 1;
1032 do_remote_copy = mc_ctl (source_desc, MCCTL_ISREMOTECOPY, 0);
1033
1034 if (!do_remote_copy) {
1035 retry_src_fstat:
1036 if (mc_fstat (source_desc, &sb)){
1037 return_status = file_error
1038 (_(" Cannot fstat source file \"%s\" \n %s "), src_path);
1039 if (return_status == FILE_RETRY)
1040 goto retry_src_fstat;
1041 do_append = 0;
1042 goto ret;
1043 }
1044 #if 0
1045 /* Im not sure if we can delete this. sb is already filled by
1046 (*xstat)() - Norbert. */
1047 } else {
1048 retry_src_rstat:
1049 if (mc_stat (src_path, &sb)){
1050 return_status = file_error
1051 (_(" Cannot stat source file \"%s\" \n %s "), src_path);
1052 if (return_status == FILE_RETRY)
1053 goto retry_src_rstat;
1054 do_append = 0;
1055 goto ret;
1056 }
1057 #endif
1058 }
1059 src_mode = sb.st_mode;
1060 #ifndef OS2_NT
1061 src_uid = sb.st_uid;
1062 src_gid = sb.st_gid;
1063 #endif
1064 utb.actime = sb.st_atime;
1065 utb.modtime = sb.st_mtime;
1066 file_size = sb.st_size;
1067
1068 /* Create the new regular file with small permissions initially,
1069 do not create a security hole. */
1070
1071 if (!do_remote_copy) {
1072 retry_dst_open:
1073 if ((do_append &&
1074 (dest_desc = mc_open (dst_path, O_WRONLY | O_APPEND)) < 0) ||
1075 (!do_append &&
1076 (dest_desc = mc_open (dst_path, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)) {
1077 return_status = file_error
1078 (_(" Cannot create target file \"%s\" \n %s "), dst_path);
1079 if (return_status == FILE_RETRY)
1080 goto retry_dst_open;
1081 do_append = 0;
1082 goto ret;
1083 }
1084 resources |= 2; /* dst_path exists/dst_path opened */
1085 resources |= 4; /* remove short file */
1086 }
1087 appending = do_append;
1088 do_append = 0;
1089
1090 if (!do_remote_copy) {
1091 retry_dst_fstat:
1092 /* Find out the optimal buffer size. */
1093 if (mc_fstat (dest_desc, &sb)){
1094 return_status = file_error
1095 (_(" Cannot fstat target file \"%s\" \n %s "), dst_path);
1096 if (return_status == FILE_RETRY)
1097 goto retry_dst_fstat;
1098 goto ret;
1099 }
1100 buf_size = 8*1024;
1101
1102 buf = (char *) xmalloc (buf_size, "copy_file_file");
1103 }
1104
1105 return_status = show_file_progress (0, file_size);
1106 mc_refresh ();
1107 if (return_status != FILE_CONT)
1108 goto ret;
1109
1110 if (!do_remote_copy){
1111 for (;;){
1112 retry_src_read:
1113 n_read = mc_read (source_desc, buf, buf_size);
1114 if (n_read < 0){
1115 return_status = file_error
1116 (_(" Cannot read source file \"%s\" \n %s "), src_path);
1117 if (return_status == FILE_RETRY)
1118 goto retry_src_read;
1119 goto ret;
1120 }
1121 if (n_read == 0)
1122 break;
1123
1124 n_read_total += n_read;
1125
1126 retry_dst_write:
1127 n_written = mc_write (dest_desc, buf, n_read);
1128 if (n_written < n_read){
1129 return_status = file_error
1130 (_(" Cannot write target file \"%s\" \n %s "), dst_path);
1131 if (return_status == FILE_RETRY)
1132 goto retry_dst_write;
1133 goto ret;
1134 }
1135 return_status = show_file_progress (n_read_total, file_size);
1136 mc_refresh ();
1137 if (return_status != FILE_CONT)
1138 goto ret;
1139 }
1140 } else {
1141 struct timeval tv_current;
1142 struct timeval tv_transfer_start;
1143 struct timeval tv_last_update;
1144 struct timeval tv_last_input;
1145 int i, size, secs, update_secs;
1146 long dt;
1147 char *stalled_msg;
1148
1149 gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
1150 tv_last_update = tv_transfer_start;
1151 eta_secs = 0.0;
1152
1153 for (i = 1; i;) {
1154 switch (size = mc_ctl (source_desc, MCCTL_REMOTECOPYCHUNK, 8192)) {
1155 case MCERR_TARGETOPEN:
1156 message_1s (1, MSG_ERROR, _(" Can't open target file "));
1157 goto ret;
1158 case MCERR_READ:
1159 goto ret;
1160 case MCERR_WRITE:
1161 message_1s (1, MSG_ERROR, _(" Can't write to local target file "));
1162 goto ret;
1163 case MCERR_DATA_ON_STDIN:
1164 break;
1165 case MCERR_FINISH:
1166 resources |= 8;
1167 i = 0;
1168 break;
1169 }
1170
1171 /* the first time we reach this line the target file has been created
1172 or truncated and we actually have a short target file.
1173 Do we really want to delete the target file when the ftp transfer
1174 fails? If we don't delete it we would be able to use reget later.
1175 (Norbert) */
1176 resources |= 4; /* remove short file */
1177
1178 if (i && size != MCERR_DATA_ON_STDIN){
1179 n_read_total += size;
1180
1181 /* Windows NT ftp servers report that files have no
1182 * permissions: -------, so if we happen to have actually
1183 * read something, we should fix the permissions.
1184 */
1185 if (!(src_mode &
1186 ((S_IRUSR|S_IWUSR|S_IXUSR) /* user */
1187 |(S_IXOTH|S_IWOTH|S_IROTH) /* other */
1188 |(S_IXGRP|S_IWGRP|S_IRGRP)))) /* group */
1189 src_mode = S_IRUSR|S_IWUSR|S_IROTH|S_IRGRP;
1190
1191 gettimeofday (&tv_last_input, NULL);
1192 }
1193 /* Timed operations: */
1194 gettimeofday (&tv_current, NULL);
1195
1196 /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
1197 secs = (tv_current.tv_sec - tv_last_update.tv_sec);
1198 if (secs > 2){
1199 rotate_dash ();
1200 tv_last_update = tv_current;
1201 }
1202
1203 /* 2. Check for a stalled condition */
1204 update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
1205 stalled_msg = "";
1206 if (update_secs > 4){
1207 stalled_msg = _("(stalled)");
1208 }
1209
1210 /* 3. Compute ETA */
1211 if (secs > 2 || eta_secs == 0.0){
1212 dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
1213
1214 if (n_read_total){
1215 eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
1216 bps = n_read_total / ((dt < 1) ? 1 : dt);
1217 } else
1218 eta_secs = 0.0;
1219 }
1220
1221 /* 4. Compute BPS rate */
1222 if (secs > 2){
1223 bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
1224 if (bps_time < 1)
1225 bps_time = 1;
1226 bps = n_read_total / bps_time;
1227 }
1228
1229 label_set_text (stalled_label, stalled_msg);
1230
1231 return_status = show_file_progress (n_read_total, file_size);
1232 mc_refresh ();
1233 if (return_status != FILE_CONT)
1234 goto ret;
1235 }
1236 }
1237 resources &= ~4; /* copy successful, don't remove target file */
1238
1239 ret:
1240 if (buf)
1241 free (buf);
1242
1243 retry_src_close:
1244 if ((resources & 1) && mc_close (source_desc) < 0){
1245 temp_status = file_error
1246 (_(" Cannot close source file \"%s\" \n %s "), src_path);
1247 if (temp_status == FILE_RETRY)
1248 goto retry_src_close;
1249 if (temp_status == FILE_ABORT)
1250 return_status = temp_status;
1251 }
1252
1253 retry_dst_close:
1254 if ((resources & 2) && mc_close (dest_desc) < 0){
1255 temp_status = file_error
1256 (_(" Cannot close target file \"%s\" \n %s "), dst_path);
1257 if (temp_status == FILE_RETRY)
1258 goto retry_dst_close;
1259 return_status = temp_status;
1260 }
1261
1262 if (resources & 4) {
1263 /* Remove short file */
1264 mc_unlink (dst_path);
1265 if (do_remote_copy) {
1266 mc_ctl (source_desc, MCCTL_FINISHREMOTE, -1);
1267 }
1268 } else if (resources & (2|8)) {
1269 /* no short file and destination file exists */
1270 #ifndef OS2_NT
1271 if (!appending && preserve_uidgid) {
1272 retry_dst_chown:
1273 if (mc_chown (dst_path, src_uid, src_gid)){
1274 temp_status = file_error
1275 (_(" Cannot chown target file \"%s\" \n %s "), dst_path);
1276 if (temp_status == FILE_RETRY)
1277 goto retry_dst_chown;
1278 return_status = temp_status;
1279 }
1280 }
1281 #endif
1282
1283 /* .ado: according to the XPG4 standard, the file must be closed before
1284 * chmod can be invoked
1285 */
1286 retry_dst_chmod:
1287 if (!appending && mc_chmod (dst_path, src_mode & umask_kill)){
1288 temp_status = file_error
1289 (_(" Cannot chmod target file \"%s\" \n %s "), dst_path);
1290 if (temp_status == FILE_RETRY)
1291 goto retry_dst_chmod;
1292 return_status = temp_status;
1293 }
1294
1295 if (!appending && preserve)
1296 mc_utime (dst_path, &utb);
1297 }
1298 return return_status;
1299 }
1300
1301 /*
1302 * I think these copy_*_* functions should have a return type.
1303 * anyway, this function *must* have two directories as arguments.
1304 */
1305 /* FIXME: This function needs to check the return values of the
1306 function calls */
1307 int
1308 copy_dir_dir (char *s, char *d, int toplevel, int move_over, int delete,
1309 struct link *parent_dirs)
1310 {
1311 #ifdef __os2__
1312 DIR *next;
1313 #else
1314 struct dirent *next;
1315 #endif
1316 struct stat buf, cbuf;
1317 DIR *reading;
1318 char *path, *mdpath, *dest_file, *dest_dir;
1319 int return_status = FILE_CONT;
1320 struct utimbuf utb;
1321 struct link *lp;
1322
1323 /* First get the mode of the source dir */
1324 retry_src_stat:
1325 if ((*xstat) (s, &cbuf)){
1326 return_status = file_error (_(" Cannot stat source directory \"%s\" \n %s "), s);
1327 if (return_status == FILE_RETRY)
1328 goto retry_src_stat;
1329 return return_status;
1330 }
1331
1332 if (is_in_linklist (dest_dirs, s, &cbuf)) {
1333 /* Don't copy a directory we created before (we don't want to copy
1334 infinitely if a directory is copied into itself) */
1335 /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
1336 return FILE_CONT;
1337 }
1338
1339 /* Hmm, hardlink to directory??? - Norbert */
1340 /* FIXME: In this step we should do something
1341 in case the destination already exist */
1342 /* Check the hardlinks */
1343 if (preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf) == 1) {
1344 /* We have made a hardlink - no more processing is necessary */
1345 return return_status;
1346 }
1347
1348 if (!S_ISDIR (cbuf.st_mode)){
1349 return_status = file_error (_(" Source directory \"%s\" is not a directory \n %s "), s);
1350 if (return_status == FILE_RETRY)
1351 goto retry_src_stat;
1352 return return_status;
1353 }
1354
1355 #ifndef OS2_NT
1356 if (is_in_linklist (parent_dirs, s, &cbuf)) {
1357 /* we found a cyclic symbolic link */
1358 message_2s (1, MSG_ERROR, _(" Cannot copy cyclic symbolic link \n `%s' "), s);
1359 return FILE_SKIP;
1360 }
1361 #endif
1362
1363 lp = xmalloc (sizeof (struct link), "parent_dirs");
1364 lp->vfs = vfs_type (s);
1365 lp->ino = cbuf.st_ino;
1366 lp->dev = cbuf.st_dev;
1367 lp->next = parent_dirs;
1368 parent_dirs = lp;
1369
1370 /* Now, check if the dest dir exists, if not, create it. */
1371 if (mc_stat (d, &buf)){
1372 /* Here the dir doesn't exist : make it !*/
1373
1374 if (move_over) {
1375 if (mc_rename (s, d) == 0) {
1376 free (parent_dirs);
1377 return FILE_CONT;
1378 }
1379 }
1380 dest_dir = copy_strings (d, 0);
1381 } else {
1382 /*
1383 * If the destination directory exists, we want to copy the whole
1384 * directory, but we only want this to happen once.
1385 *
1386 * Escape sequences added to the * to avoid compiler warnings.
1387 * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
1388 * or ( /bla doesn't exist ) /tmp/\* to /bla -> /bla/\*
1389 */
1390 #if 1
1391 /* Again, I'm getting curious. Is not d already what we wanted, incl.
1392 * masked source basename? Is not this just a relict of the past versions?
1393 * I'm afraid this will lead into a two level deep dive :(
1394 *
1395 * I think this is indeed the problem. I can not remember any case where
1396 * we actually would like that behaviour -miguel
1397 *
1398 * It's a documented feature (option `Dive into subdir if exists' in the
1399 * copy/move dialog). -Norbert
1400 */
1401 if (toplevel && dive_into_subdirs){
1402 dest_dir = concat_dir_and_file (d, x_basename (s));
1403 } else
1404 #endif
1405 {
1406 dest_dir = copy_strings (d, 0);
1407 goto dont_mkdir;
1408 }
1409 }
1410 retry_dst_mkdir:
1411 if (my_mkdir (dest_dir, (cbuf.st_mode & umask_kill) | S_IRWXU)){
1412 return_status = file_error (_(" Cannot create target directory \"%s\" \n %s "), dest_dir);
1413 if (return_status == FILE_RETRY)
1414 goto retry_dst_mkdir;
1415 goto ret;
1416 }
1417
1418 lp = xmalloc (sizeof (struct link), "dest_dirs");
1419 mc_stat (dest_dir, &buf);
1420 lp->vfs = vfs_type (dest_dir);
1421 lp->ino = buf.st_ino;
1422 lp->dev = buf.st_dev;
1423 lp->next = dest_dirs;
1424 dest_dirs = lp;
1425
1426 #ifndef OS2_NT
1427 if (preserve_uidgid) {
1428 retry_dst_chown:
1429 if (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid)){
1430 return_status = file_error
1431 (_(" Cannot chown target directory \"%s\" \n %s "), dest_dir);
1432 if (return_status == FILE_RETRY)
1433 goto retry_dst_chown;
1434 goto ret;
1435 }
1436 }
1437 #endif
1438
1439 dont_mkdir:
1440 /* open the source dir for reading */
1441 if ((reading = mc_opendir (s)) == 0){
1442 goto ret;
1443 }
1444
1445 while ((next = mc_readdir (reading)) && return_status != FILE_ABORT){
1446 /*
1447 * Now, we don't want '.' and '..' to be created / copied at any time
1448 */
1449 if (!strcmp (next->d_name, "."))
1450 continue;
1451 if (!strcmp (next->d_name, ".."))
1452 continue;
1453
1454 /* get the filename and add it to the src directory */
1455 path = concat_dir_and_file (s, next->d_name);
1456
1457 (*xstat)(path, &buf);
1458 if (S_ISDIR (buf.st_mode)){
1459 mdpath = concat_dir_and_file (dest_dir, next->d_name);
1460 /*
1461 * From here, we just intend to recursively copy subdirs, not
1462 * the double functionality of copying different when the target
1463 * dir already exists. So, we give the recursive call the flag 0
1464 * meaning no toplevel.
1465 */
1466 return_status = copy_dir_dir (path, mdpath, 0, 0, delete, parent_dirs);
1467 free (mdpath);
1468 } else {
1469 dest_file = concat_dir_and_file (dest_dir, x_basename (path));
1470 return_status = copy_file_file (path, dest_file, 1);
1471 free (dest_file);
1472 }
1473 if (delete && return_status == FILE_CONT) {
1474 if (erase_at_end) {
1475 static struct link *tail;
1476 lp = xmalloc (sizeof (struct link) + strlen (path), "erase_list");
1477 strcpy (lp->name, path);
1478 lp->st_mode = buf.st_mode;
1479 lp->next = 0;
1480 if (erase_list) {
1481 tail->next = lp;
1482 tail = lp;
1483 } else
1484 erase_list = tail = lp;
1485 } else {
1486 if (S_ISDIR (buf.st_mode)) {
1487 return_status = erase_dir_iff_empty (path);
1488 } else
1489 return_status = erase_file (path);
1490 }
1491 }
1492
1493 #ifdef __os2__
1494 /* The OS/2 mc_readdir returns a block of memory DIR
1495 * next should be freed: .ado
1496 */
1497 if (!next)
1498 free (next);
1499 #endif
1500 free (path);
1501 }
1502 mc_closedir (reading);
1503
1504 /* .ado: Directories can not have permission set in OS/2 */
1505 #ifndef __os2__
1506 if (preserve) {
1507 mc_chmod (dest_dir, cbuf.st_mode & umask_kill);
1508 utb.actime = cbuf.st_atime;
1509 utb.modtime = cbuf.st_mtime;
1510 mc_utime(dest_dir, &utb);
1511 }
1512
1513 #endif
1514 ret:
1515 free (dest_dir);
1516 free (parent_dirs);
1517 return return_status;
1518 }
1519
1520 /* }}} */
1521
1522 /* {{{ Move routines */
1523
1524 int
1525 move_file_file (char *s, char *d)
1526 {
1527 struct stat src_stats, dst_stats;
1528 int return_status = FILE_CONT;
1529
1530 if (show_source (s) == FILE_ABORT
1531 || show_target (d) == FILE_ABORT)
1532 return FILE_ABORT;
1533
1534 mc_refresh ();
1535
1536 retry_src_lstat:
1537 if (mc_lstat (s, &src_stats) != 0){
1538 /* Source doesn't exist */
1539 return_status = file_error (_(" Cannot stat file \"%s\" \n %s "), s);
1540 if (return_status == FILE_RETRY)
1541 goto retry_src_lstat;
1542 return return_status;
1543 }
1544
1545 if (mc_lstat (d, &dst_stats) == 0){
1546 /* Destination already exists */
1547 /* .ado: for OS/2 and NT, no st_ino exists */
1548 #ifndef OS2_NT
1549 if (src_stats.st_dev == dst_stats.st_dev
1550 && src_stats.st_ino == dst_stats.st_ino){
1551 int msize = COLS - 36;
1552 char st[MC_MAXPATHLEN];
1553 char dt[MC_MAXPATHLEN];
1554
1555 if (msize < 0)
1556 msize = 40;
1557 msize /= 2;
1558
1559 strcpy (st, name_trunc (s, msize));
1560 strcpy (dt, name_trunc (d, msize));
1561 message_3s (1, MSG_ERROR, _(" `%s' and `%s' are the same file "),
1562 st, dt );
1563 do_refresh ();
1564 return FILE_SKIP;
1565 }
1566 #endif /* OS2_NT */
1567 if (S_ISDIR (dst_stats.st_mode)){
1568 message_2s (1, MSG_ERROR, _(" Cannot overwrite directory `%s' "), d);
1569 do_refresh ();
1570 return FILE_SKIP;
1571 }
1572
1573 if (confirm_overwrite){
1574 if (vfs_file_is_ftp (s))
1575 do_reget = -1;
1576 else
1577 do_reget = 0;
1578
1579 return_status = query_replace (d, &src_stats, &dst_stats);
1580 if (return_status != FILE_CONT)
1581 return return_status;
1582 }
1583 /* Ok to overwrite */
1584 }
1585 #if 0
1586 retry_rename:
1587 #endif
1588 if (!do_append) {
1589 if (S_ISLNK (src_stats.st_mode) && stable_symlinks) {
1590 if ((return_status = make_symlink (s, d)) == FILE_CONT)
1591 goto retry_src_remove;
1592 else
1593 return return_status;
1594 }
1595
1596 if (mc_rename (s, d) == 0)
1597 return FILE_CONT;
1598 }
1599 #if 0
1600 /* Comparison to EXDEV seems not to work in nfs if you're moving from
1601 one nfs to the same, but on the server it is on two different
1602 filesystems. Then nfs returns EIO instead of EXDEV.
1603 Hope it will not hurt if we always in case of error try to copy/delete. */
1604 else
1605 errno = EXDEV; /* Hack to copy (append) the file and then delete it */
1606
1607 if (errno != EXDEV){
1608 return_status = files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s, d);
1609 if (return_status == FILE_RETRY)
1610 goto retry_rename;
1611 return return_status;
1612 }
1613 #endif
1614
1615 /* Failed because filesystem boundary -> copy the file instead */
1616 if ((return_status = copy_file_file (s, d, 0)) != FILE_CONT)
1617 return return_status;
1618 if ((return_status = show_source (NULL)) != FILE_CONT
1619 || (return_status = show_file_progress (0, 0)) != FILE_CONT)
1620 return return_status;
1621
1622 mc_refresh ();
1623
1624 retry_src_remove:
1625 if (mc_unlink (s)){
1626 return_status = file_error (_(" Cannot remove file \"%s\" \n %s "), s);
1627 if (return_status == FILE_RETRY)
1628 goto retry_src_remove;
1629 return return_status;
1630 }
1631
1632 return FILE_CONT;
1633 }
1634
1635 int
1636 move_dir_dir (char *s, char *d)
1637 {
1638 struct stat sbuf, dbuf, destbuf;
1639 struct link *lp;
1640 char *destdir;
1641 int return_status;
1642 int move_over = 0;
1643
1644 if (show_source (s) == FILE_ABORT
1645 || show_target (d) == FILE_ABORT)
1646 return FILE_ABORT;
1647
1648 mc_refresh ();
1649
1650 mc_stat (s, &sbuf);
1651 if (mc_stat (d, &dbuf))
1652 destdir = copy_strings (d, 0); /* destination doesn't exist */
1653 else if (!dive_into_subdirs) {
1654 destdir = copy_strings (d, 0);
1655 move_over = 1;
1656 } else
1657 destdir = concat_dir_and_file (d, x_basename (s));
1658
1659 /* Check if the user inputted an existing dir */
1660 retry_dst_stat:
1661 if (!mc_stat (destdir, &destbuf)){
1662 if (move_over) {
1663 if ((return_status = copy_dir_dir (s, destdir, 0, 1, 1, 0)) != FILE_CONT)
1664 goto ret;
1665 goto oktoret;
1666 } else {
1667 if (S_ISDIR (destbuf.st_mode))
1668 return_status = file_error (_(" Cannot overwrite directory \"%s\" %s "), destdir);
1669 else
1670 return_status = file_error (_(" Cannot overwrite file \"%s\" %s "), destdir);
1671 if (return_status == FILE_RETRY)
1672 goto retry_dst_stat;
1673 }
1674 free (destdir);
1675 return return_status;
1676 }
1677
1678 retry_rename:
1679 if (mc_rename (s, destdir) == 0){
1680 return_status = FILE_CONT;
1681 goto ret;
1682 }
1683 /* .ado: Drive, Do we need this anymore? */
1684 #ifdef WIN32
1685 else {
1686 /* EXDEV: cross device; does not work everywhere */
1687 if (toupper(s[0]) != toupper(destdir[0]))
1688 goto w32try;
1689 }
1690 #endif
1691
1692 if (errno != EXDEV){
1693 return_status = files_error (_(" Cannot move directory \"%s\" to \"%s\" \n %s "), s, d);
1694 if (return_status == FILE_RETRY)
1695 goto retry_rename;
1696 goto ret;
1697 }
1698
1699 w32try:
1700 /* Failed because of filesystem boundary -> copy dir instead */
1701 if ((return_status = copy_dir_dir (s, destdir, 0, 0, 1, 0)) != FILE_CONT)
1702 goto ret;
1703 oktoret:
1704 if ((return_status = show_source (NULL)) != FILE_CONT
1705 || (return_status = show_file_progress (0, 0)) != FILE_CONT)
1706 goto ret;
1707
1708 mc_refresh ();
1709 if (erase_at_end) {
1710 for ( ; erase_list && return_status != FILE_ABORT; ) {
1711 if (S_ISDIR (erase_list->st_mode)) {
1712 return_status = erase_dir_iff_empty (erase_list->name);
1713 } else
1714 return_status = erase_file (erase_list->name);
1715 lp = erase_list;
1716 erase_list = erase_list->next;
1717 free (lp);
1718 }
1719 }
1720 erase_dir_iff_empty (s);
1721
1722 ret:
1723 free (destdir);
1724 for ( ; erase_list; ) {
1725 lp = erase_list;
1726 erase_list = erase_list->next;
1727 free (lp);
1728 }
1729 return return_status;
1730 }
1731
1732 /* }}} */
1733
1734 /* {{{ Erase routines */
1735
1736 static int
1737 erase_file (char *s)
1738 {
1739 int return_status;
1740
1741 if (show_deleting (s) == FILE_ABORT)
1742 return FILE_ABORT;
1743
1744 mc_refresh ();
1745
1746 retry_unlink:
1747 if (mc_unlink (s)){
1748 return_status = file_error (_(" Cannot delete file \"%s\" \n %s "), s);
1749 if (return_status == FILE_RETRY)
1750 goto retry_unlink;
1751 return return_status;
1752 }
1753 return FILE_CONT;
1754 }
1755
1756 static int
1757 recursive_erase (char *s)
1758 {
1759 struct dirent *next;
1760 struct stat buf;
1761 DIR *reading;
1762 char *path;
1763 int return_status = FILE_CONT;
1764
1765 if (!strcmp (s, ".."))
1766 return 1;
1767
1768 reading = mc_opendir (s);
1769
1770 if (!reading)
1771 return 1;
1772
1773 while ((next = mc_readdir (reading)) && return_status == FILE_CONT){
1774 if (!strcmp (next->d_name, "."))
1775 continue;
1776 if (!strcmp (next->d_name, ".."))
1777 continue;
1778 path = concat_dir_and_file (s, next->d_name);
1779 if (mc_lstat (path, &buf)){
1780 free (path);
1781 return 1;
1782 }
1783 if (S_ISDIR (buf.st_mode))
1784 return_status = (recursive_erase (path) != FILE_CONT);
1785 else
1786 return_status = erase_file (path);
1787 free (path);
1788 /* .ado: OS/2 returns a block of memory DIR to next and must be freed */
1789 #ifdef __os2__
1790 if (!next)
1791 free (next);
1792 #endif
1793 }
1794 mc_closedir (reading);
1795 if (return_status != FILE_CONT)
1796 return return_status;
1797 if (show_deleting (s) == FILE_ABORT)
1798 return FILE_ABORT;
1799 mc_refresh ();
1800 retry_rmdir:
1801 if (my_rmdir (s)){
1802 return_status = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1803 if (return_status == FILE_RETRY)
1804 goto retry_rmdir;
1805 return return_status;
1806 }
1807 return FILE_CONT;
1808 }
1809
1810 /* Return -1 on error, 1 if there are no entries besides "." and ".."
1811 in the directory path points to, 0 else. */
1812 static int
1813 check_dir_is_empty(char *path)
1814 {
1815 DIR *dir;
1816 struct dirent *d;
1817 int i;
1818
1819 dir = mc_opendir (path);
1820 if (!dir)
1821 return -1;
1822
1823 for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir)) {
1824 if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
1825 (d->d_name[1] == '.' && d->d_name[2] == '\0')))
1826 continue; /* "." or ".." */
1827 i = 0;
1828 break;
1829 }
1830
1831 mc_closedir (dir);
1832 return i;
1833 }
1834
1835 int
1836 erase_dir (char *s)
1837 {
1838 int error;
1839
1840 if (strcmp (s, "..") == 0)
1841 return FILE_SKIP;
1842
1843 if (strcmp (s, ".") == 0)
1844 return FILE_SKIP;
1845
1846 if (show_deleting (s) == FILE_ABORT)
1847 return FILE_ABORT;
1848 mc_refresh ();
1849
1850 /* The old way to detect a non empty directory was:
1851 error = my_rmdir (s);
1852 if (error && (errno == ENOTEMPTY || errno == EEXIST))) {
1853 For the linux user space nfs server (nfs-server-2.2beta29-2)
1854 we would have to check also for EIO. I hope the new way is
1855 fool proof. (Norbert)
1856 */
1857 error = check_dir_is_empty (s);
1858 if (error == 0) { /* not empty */
1859 error = query_recursive (s);
1860 if (error == FILE_CONT)
1861 return recursive_erase (s);
1862 else
1863 return error;
1864 }
1865
1866 retry_rmdir:
1867 error = my_rmdir (s);
1868 if (error == -1){
1869 error = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1870 if (error == FILE_RETRY)
1871 goto retry_rmdir;
1872 return error;
1873 }
1874 return FILE_CONT;
1875 }
1876
1877 int
1878 erase_dir_iff_empty (char *s)
1879 {
1880 int error;
1881
1882 if (strcmp (s, "..") == 0)
1883 return FILE_SKIP;
1884
1885 if (strcmp (s, ".") == 0)
1886 return FILE_SKIP;
1887
1888 if (show_deleting (s) == FILE_ABORT)
1889 return FILE_ABORT;
1890 mc_refresh ();
1891
1892 if (1 != check_dir_is_empty (s)) /* not empty or error */
1893 return FILE_CONT;
1894
1895 retry_rmdir:
1896 error = my_rmdir (s);
1897 if (error) {
1898 error = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
1899 if (error == FILE_RETRY)
1900 goto retry_rmdir;
1901 return error;
1902 }
1903 return FILE_CONT;
1904 }
1905
1906 /* }}} */
1907
1908 /* {{{ Panel operate routines */
1909
1910 /* Returns currently selected file or the first marked file if there is one */
1911 static char *
1912 get_file (WPanel *panel, struct stat *stat_buf)
1913 {
1914 int i;
1915
1916 /* No problem with Gnome, as get_current_type never returns view_tree there */
1917 if (get_current_type () == view_tree){
1918 WTree *tree = (WTree *)get_panel_widget (get_current_index ());
1919
1920 mc_stat (tree->selected_ptr->name, stat_buf);
1921 return tree->selected_ptr->name;
1922 }
1923
1924 if (panel->marked){
1925 for (i = 0; i < panel->count; i++)
1926 if (panel->dir.list [i].f.marked){
1927 *stat_buf = panel->dir.list [i].buf;
1928 return panel->dir.list [i].fname;
1929 }
1930 } else {
1931 *stat_buf = panel->dir.list [panel->selected].buf;
1932 return panel->dir.list [panel->selected].fname;
1933 }
1934 fprintf (stderr, _(" Internal error: get_file \n"));
1935 mi_getch ();
1936 return "";
1937 }
1938
1939 static int
1940 is_wildcarded (char *p)
1941 {
1942 for (; *p; p++) {
1943 if (*p == '*')
1944 return 1;
1945 else if (*p == '\\' && p [1] >= '1' && p [1] <= '9')
1946 return 1;
1947 }
1948 return 0;
1949 }
1950
1951 /* Sets all global variables used by copy_file_file/move_file_file to a
1952 resonable default
1953 (file_mask_dialog sets these global variables interactively)
1954 */
1955 void
1956 file_mask_defaults (void)
1957 {
1958 stable_symlinks = 0;
1959 op_follow_links = 0;
1960 dive_into_subdirs = 0;
1961 xstat = mc_lstat;
1962
1963 preserve = 1;
1964 umask_kill = 0777777;
1965 preserve_uidgid = (geteuid () == 0) ? 1 : 0;
1966 }
1967
1968 #define FMDY 13
1969 #define FMD_XLEN 64
1970 static int fmd_xlen = FMD_XLEN, fmd_i18n_flag = 0;
1971 static QuickWidget fmd_widgets [] = {
1972
1973 #define FMCB0 FMDC
1974 #define FMCB12 0
1975 #define FMCB11 1
1976 /* follow symlinks and preserve Attributes must be the first */
1977 { quick_checkbox, 3, 64, 8, FMDY, N_("preserve &Attributes"), 9, 0,
1978 &op_preserve, 0, XV_WLAY_BELOWCLOSE, "preserve" },
1979 { quick_checkbox, 3, 64, 7, FMDY, N_("follow &Links"), 7, 0,
1980 &op_follow_links, 0, XV_WLAY_BELOWCLOSE, "follow" },
1981 #ifdef HAVE_XVIEW
1982 #define FMDI1 5
1983 #define FMDI2 2
1984 #define FMDC 4
1985 { quick_input, 3, 64, 6, FMDY, "", 58, 0,
1986 0, 0, XV_WLAY_BELOWCLOSE, "input2" },
1987 #endif
1988 { quick_label, 3, 64, 5, FMDY, N_("to:"), 0, 0, 0, 0, XV_WLAY_BELOWOF,"to"},
1989 { quick_checkbox, 37, 64, 4, FMDY, N_("&Using shell patterns"), 0, 0,
1990 0/* &source_easy_patterns */, 0, XV_WLAY_BELOWCLOSE, "using-shell" },
1991 { quick_input, 3, 64, 3, FMDY, "", 58,
1992 0, 0, 0, XV_WLAY_BELOWCLOSE, "input-def" },
1993 #ifndef HAVE_XVIEW
1994 #define FMDI1 4
1995 #define FMDI2 5
1996 #define FMDC 3
1997 { quick_input, 3, 64, 6, FMDY, "", 58, 0,
1998 0, 0, XV_WLAY_BELOWCLOSE, "input2" },
1999 #endif
2000 #define FMDI0 6
2001 { quick_label, 3, 64, 2, FMDY, "", 0, 0, 0, 0, XV_WLAY_DONTCARE, "ql" },
2002 #define FMBRGT 7
2003 { quick_button, 42, 64, 9, FMDY, N_("&Cancel"), 0, B_CANCEL, 0, 0, XV_WLAY_DONTCARE,
2004 "cancel" },
2005 #undef SKIP
2006 #ifdef WITH_BACKGROUND
2007 # define SKIP 5
2008 # define FMCB21 11
2009 # define FMCB22 10
2010 # define FMBLFT 9
2011 # define FMBMID 8
2012 { quick_button, 25, 64, 9, FMDY, N_("&Background"), 0, B_USER, 0, 0, XV_WLAY_DONTCARE, "back" },
2013 #else /* WITH_BACKGROUND */
2014 # define SKIP 4
2015 # define FMCB21 10
2016 # define FMCB22 9
2017 # define FMBLFT 8
2018 # undef FMBMID
2019 #endif
2020 { quick_button, 14, 64, 9, FMDY, N_("&Ok"), 0, B_ENTER, 0, 0, XV_WLAY_NEXTROW, "ok" },
2021 { quick_checkbox, 42, 64, 8, FMDY, N_("&Stable Symlinks"), 0, 0,
2022 &stable_symlinks, 0, XV_WLAY_BELOWCLOSE, "stab-sym" },
2023 { quick_checkbox, 31, 64, 7, FMDY, N_("&Dive into subdir if exists"), 0, 0,
2024 &dive_into_subdirs, 0, XV_WLAY_BELOWOF, "dive" },
2025 { 0 } };
2026
2027 void
2028 fmd_init_i18n()
2029 {
2030 #ifdef ENABLE_NLS
2031
2032 register int i;
2033 int len;
2034
2035 for (i = sizeof (op_names) / sizeof (op_names[0]); i--;)
2036 op_names [i] = _(op_names [i]);
2037
2038 i = sizeof (fmd_widgets) / sizeof (fmd_widgets [0]) - 1;
2039 while (i--)
2040 if (fmd_widgets [i].text[0] != '\0')
2041 fmd_widgets [i].text = _(fmd_widgets [i].text);
2042
2043 len = strlen (fmd_widgets [FMCB11].text)
2044 + strlen (fmd_widgets [FMCB21].text) + 15;
2045 fmd_xlen = max (fmd_xlen, len);
2046
2047 len = strlen (fmd_widgets [FMCB12].text)
2048 + strlen (fmd_widgets [FMCB22].text) + 15;
2049 fmd_xlen = max (fmd_xlen, len);
2050
2051 len = strlen (fmd_widgets [FMBRGT].text)
2052 + strlen (fmd_widgets [FMBLFT].text) + 11;
2053
2054 #ifdef FMBMID
2055 len += strlen (fmd_widgets [FMBMID].text) + 6;
2056 #endif
2057
2058 fmd_xlen = max (fmd_xlen, len + 4);
2059
2060 len = (fmd_xlen - (len + 6)) / 2;
2061 i = fmd_widgets [FMBLFT].relative_x = len + 3;
2062 i += strlen (fmd_widgets [FMBLFT].text) + 8;
2063
2064 #ifdef FMBMID
2065 fmd_widgets [FMBMID].relative_x = i;
2066 i += strlen (fmd_widgets [FMBMID].text) + 6;
2067 #endif
2068
2069 fmd_widgets [FMBRGT].relative_x = i;
2070
2071 #define chkbox_xpos(i) \
2072 fmd_widgets [i].relative_x = fmd_xlen - strlen (fmd_widgets [i].text) - 6
2073
2074 chkbox_xpos(FMCB0);
2075 chkbox_xpos(FMCB21);
2076 chkbox_xpos(FMCB22);
2077
2078 if (fmd_xlen != FMD_XLEN)
2079 {
2080 i = sizeof (fmd_widgets) / sizeof (fmd_widgets [0]) - 1;
2081 while (i--)
2082 fmd_widgets [i].x_divisions = fmd_xlen;
2083
2084 fmd_widgets [FMDI1].hotkey_pos =
2085 fmd_widgets [FMDI2].hotkey_pos = fmd_xlen - 6;
2086 }
2087 #undef chkbox_xpos
2088 #endif /* ENABLE_NLS */
2089
2090 fmd_i18n_flag = 1;
2091 }
2092
2093 char *
2094 file_mask_dialog (int operation, char *text, char *def_text, int only_one, int *do_background)
2095 {
2096 int source_easy_patterns = easy_patterns;
2097 char *source_mask, *orig_mask, *dest_dir;
2098 const char *error;
2099 struct stat buf;
2100 int val;
2101
2102 QuickDialog Quick_input;
2103
2104 if (!fmd_i18n_flag)
2105 fmd_init_i18n();
2106
2107 stable_symlinks = 0;
2108 fmd_widgets [FMDC].result = &source_easy_patterns;
2109 fmd_widgets [FMDI1].text = easy_patterns ? "*" : "^\\(.*\\)$";
2110 Quick_input.xlen = fmd_xlen;
2111 Quick_input.xpos = -1;
2112 Quick_input.title = op_names [operation];
2113 Quick_input.help = "[Mask Copy/Rename]";
2114 Quick_input.ylen = FMDY;
2115 Quick_input.i18n = 1;
2116
2117 if (operation == OP_COPY) {
2118 Quick_input.class = "quick_file_mask_copy";
2119 Quick_input.widgets = fmd_widgets;
2120 } else { /* operation == OP_MOVE */
2121 Quick_input.class = "quick_file_mask_move";
2122 Quick_input.widgets = fmd_widgets + 2;
2123 }
2124 fmd_widgets [FMDI0].text = text;
2125 fmd_widgets [FMDI2].text = def_text;
2126 fmd_widgets [FMDI2].str_result = &dest_dir;
2127 fmd_widgets [FMDI1].str_result = &source_mask;
2128
2129 *do_background = 0;
2130 ask_file_mask:
2131
2132 if ((val = quick_dialog_skip (&Quick_input, SKIP)) == B_CANCEL)
2133 return 0;
2134
2135 if (op_follow_links && operation != OP_MOVE)
2136 xstat = mc_stat;
2137 else
2138 xstat = mc_lstat;
2139
2140 if (op_preserve || operation == OP_MOVE) {
2141 preserve = 1;
2142 umask_kill = 0777777;
2143 preserve_uidgid = (geteuid () == 0) ? 1 : 0;
2144 }
2145 else {
2146 int i;
2147 preserve = preserve_uidgid = 0;
2148 i = umask (0);
2149 umask (i);
2150 umask_kill = i ^ 0777777;
2151 }
2152
2153 orig_mask = source_mask;
2154 if (!dest_dir || !*dest_dir) {
2155 if (source_mask)
2156 free (source_mask);
2157 return dest_dir;
2158 }
2159 if (source_easy_patterns) {
2160 source_easy_patterns = easy_patterns;
2161 easy_patterns = 1;
2162 source_mask = convert_pattern (source_mask, match_file, 1);
2163 easy_patterns = source_easy_patterns;
2164 error = re_compile_pattern (source_mask, strlen (source_mask), &rx);
2165 free (source_mask);
2166 } else
2167 error = re_compile_pattern (source_mask, strlen (source_mask), &rx);
2168
2169 if (error) {
2170 message_3s (1, MSG_ERROR, _("Invalid source pattern `%s' \n %s "),
2171 orig_mask, error);
2172 if (orig_mask)
2173 free (orig_mask);
2174 goto ask_file_mask;
2175 }
2176 if (orig_mask)
2177 free (orig_mask);
2178 dest_mask = strrchr (dest_dir, PATH_SEP);
2179 if (dest_mask == NULL)
2180 dest_mask = dest_dir;
2181 else
2182 dest_mask++;
2183 orig_mask = dest_mask;
2184 if (!*dest_mask || (!dive_into_subdirs && !is_wildcarded (dest_mask) &&
2185 (!only_one || (!mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode)))) ||
2186 (dive_into_subdirs && ((!only_one && !is_wildcarded (dest_mask)) ||
2187 (only_one && !mc_stat (dest_dir, &buf) && S_ISDIR (buf.st_mode)))))
2188 dest_mask = strdup ("*");
2189 else {
2190 dest_mask = strdup (dest_mask);
2191 *orig_mask = 0;
2192 }
2193 if (!*dest_dir) {
2194 free (dest_dir);
2195 dest_dir = strdup ("./");
2196 }
2197 if (val == B_USER)
2198 *do_background = 1;
2199 return dest_dir;
2200 }
2201
2202 /*
2203 * This array introduced to avoid translation problems. The former (op_names)
2204 * is assumed to be nouns, suitable in dialog box titles; this one should
2205 * contain whatever is used in prompt itself (i.e. in russian, it's verb).
2206 * Notice first symbol - it is to fool gettext and force these strings to
2207 * be different for it. First symbol is skipped while building a prompt.
2208 * (I don't use spaces around the words, because someday they could be
2209 * dropped, when widgets get smarter)
2210 */
2211 static char *op_names1 [] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
2212
2213 /*
2214 * These are formats for building a prompt. Parts encoded as follows:
2215 * %o - operation from op_names1
2216 * %f - file/files or files/directories, as appropriate
2217 * %m - "with source mask" or question mark for delete
2218 * %s - source name (truncated)
2219 * %d - number of marked files
2220 */
2221 static char* one_format = N_("%o %f \"%s\"%m");
2222 static char* many_format = N_("%o %d %f%m");
2223
2224 static char* prompt_parts [] =
2225 {
2226 N_("file"), N_("files"), N_("directory"), N_("directories"),
2227 N_("files/directories"), N_(" with source mask:")
2228 };
2229
2230 static char*
2231 generate_prompt(char* cmd_buf, WPanel* panel, int operation, int only_one,
2232 struct stat* src_stat)
2233 {
2234 register char *sp, *cp;
2235 register int i;
2236 char format_string [200];
2237 char *dp = format_string;
2238 char* source = NULL;
2239
2240 #ifdef ENABLE_NLS
2241 static int i18n_flag = 0;
2242 if (!i18n_flag)
2243 {
2244 if (!fmd_i18n_flag)
2245 fmd_init_i18n(); /* to get proper fmd_xlen */
2246
2247 for (i = sizeof (op_names1) / sizeof (op_names1 [0]); i--;)
2248 op_names1 [i] = _(op_names1 [i]);
2249
2250 for (i = sizeof (prompt_parts) / sizeof (prompt_parts [0]); i--;)
2251 prompt_parts [i] = _(prompt_parts [i]);
2252
2253 one_format = _(one_format);
2254 many_format = _(many_format);
2255 i18n_flag = 1;
2256 }
2257 #endif /* ENABLE_NLS */
2258
2259 sp = only_one ? one_format : many_format;
2260
2261 if (only_one)
2262 source = get_file (panel, src_stat);
2263
2264 while (*sp)
2265 {
2266 switch (*sp)
2267 {
2268 case '%':
2269 cp = NULL;
2270 switch (sp[1])
2271 {
2272 case 'o':
2273 cp = op_names1 [operation] + 1;
2274 break;
2275 case 'm':
2276 cp = operation == OP_DELETE ? "?" : prompt_parts [5];
2277 break;
2278 case 'f':
2279 if (only_one)
2280 {
2281 cp = S_ISDIR (src_stat->st_mode) ?
2282 prompt_parts [2] : prompt_parts [0];
2283 }
2284 else
2285 {
2286 cp = (panel->marked == panel->dirs_marked)
2287 ? prompt_parts [3]
2288 : (panel->dirs_marked ? prompt_parts [4]
2289 : prompt_parts [1]);
2290 }
2291 break;
2292 default:
2293 *dp++ = *sp++;
2294 }
2295 if (cp)
2296 {
2297 sp += 2;
2298 while (*cp)
2299 *dp++ = *cp++;
2300 }
2301 break;
2302 default:
2303 *dp++ = *sp++;
2304 }
2305 }
2306 *dp = '\0';
2307
2308 if (only_one)
2309 {
2310 i = fmd_xlen - strlen(format_string) - 4;
2311 sprintf (cmd_buf, format_string, name_trunc (source, i));
2312 }
2313 else
2314 {
2315 sprintf (cmd_buf, format_string, panel->marked);
2316 i = strlen (cmd_buf) + 6 - fmd_xlen;
2317 if (i > 0)
2318 {
2319 fmd_xlen += i;
2320 fmd_init_i18n(); /* to recalculate positions of child widgets */
2321 }
2322 }
2323
2324 return source;
2325 }
2326
2327
2328 /* Returns 1 if did change the directory structure,
2329 Returns 0 if user aborted */
2330 int
2331 panel_operate (void *source_panel, int operation, char *thedefault)
2332 {
2333 WPanel *panel = source_panel;
2334 #ifdef WITH_FULL_PATHS
2335 char *source_with_path = NULL;
2336 #else
2337 # define source_with_path source
2338 #endif
2339 char *source = NULL;
2340 char *dest = NULL;
2341 char *temp = NULL;
2342 int only_one = (get_current_type () == view_tree) || (panel->marked <= 1);
2343 struct stat src_stat, dst_stat;
2344 int i, value;
2345 long marked, total;
2346 long count = 0, bytes = 0;
2347 int dst_result;
2348 int do_bg; /* do background operation? */
2349
2350 do_bg = 0;
2351 rx.buffer = NULL;
2352 free_linklist (&linklist);
2353 free_linklist (&dest_dirs);
2354 if (get_current_type () == view_listing)
2355 if (!panel->marked && !strcmp (selection (panel)->fname, "..")){
2356 message (1, MSG_ERROR, _(" Can't operate on \"..\"! "));
2357 return 0;
2358 }
2359
2360 if (operation < OP_COPY || operation > OP_DELETE)
2361 return 0;
2362
2363 /* Generate confirmation prompt */
2364 source = generate_prompt(cmd_buf, panel, operation, only_one, &src_stat);
2365
2366 /* Show confirmation dialog */
2367 if (operation == OP_DELETE && confirm_delete){
2368 if (know_not_what_am_i_doing)
2369 query_set_sel (1);
2370 i = query_dialog (_(op_names [operation]), cmd_buf,
2371 D_ERROR, 2, _("&Yes"), _("&No"));
2372 if (i != 0)
2373 return 0;
2374 } else if (operation != OP_DELETE) {
2375 char *dest_dir;
2376
2377 if (thedefault != NULL)
2378 dest_dir = thedefault;
2379 else if (get_other_type () == view_listing)
2380 dest_dir = opanel->cwd;
2381 else
2382 dest_dir = panel->cwd;
2383
2384 rx.buffer = (char *) xmalloc (MC_MAXPATHLEN, "mask copying");
2385 rx.allocated = MC_MAXPATHLEN;
2386 rx.translate = 0;
2387 dest = file_mask_dialog (operation, cmd_buf, dest_dir, only_one, &do_bg);
2388 if (!dest) {
2389 free (rx.buffer);
2390 return 0;
2391 }
2392 if (!*dest){
2393 free (rx.buffer);
2394 free (dest);
2395 return 0;
2396 }
2397 }
2398
2399 #ifdef WITH_BACKGROUND
2400 /* Did the user select to do a background operation? */
2401 if (do_bg){
2402 int v;
2403
2404 v = do_background (copy_strings (operation_names [operation], ": ", panel->cwd, 0));
2405 if (v == -1){
2406 message (1, MSG_ERROR, _(" Sorry, I could not put the job in background "));
2407 }
2408
2409 /* If we are the parent */
2410 if (v == 1){
2411 vfs_force_expire (panel->cwd);
2412 vfs_force_expire (dest);
2413 return 0;
2414 }
2415 }
2416 #endif
2417 /* Initialize things */
2418 /* We turn on ETA display if the source is an ftp file system */
2419 create_op_win (operation, vfs_file_is_ftp (panel->cwd));
2420 ftpfs_hint_reread (0);
2421
2422 /* Now, let's do the job */
2423 /* This code is only called by the tree and panel code */
2424 if (only_one){
2425 /* One file: FIXME mc_chdir will take user out of any vfs */
2426 if (operation != OP_COPY && get_current_type () == view_tree)
2427 mc_chdir (PATH_SEP_STR);
2428
2429 /* The source and src_stat variables have been initialized before */
2430 #ifdef WITH_FULL_PATHS
2431 source_with_path = concat_dir_and_file (panel->cwd, source);
2432 #endif
2433
2434 if (operation == OP_DELETE){
2435 /* Delete operation */
2436 if (S_ISDIR (src_stat.st_mode))
2437 value = erase_dir (source_with_path);
2438 else
2439 value = erase_file (source_with_path);
2440 } else {
2441 /* Copy or move operation */
2442 temp = transform_source (source_with_path);
2443 if (temp == NULL) {
2444 value = transform_error;
2445 } else {
2446 temp = get_full_name (dest, temp);
2447 free (dest);
2448 dest = temp;
2449 temp = 0;
2450
2451 switch (operation){
2452 case OP_COPY:
2453 /* we use op_follow_links only with OP_COPY,
2454 */
2455 (*xstat) (source_with_path, &src_stat);
2456 if (S_ISDIR (src_stat.st_mode))
2457 value = copy_dir_dir (source_with_path, dest, 1, 0, 0, 0);
2458 else
2459 value = copy_file_file (source_with_path, dest, 1);
2460 break;
2461 case OP_MOVE:
2462 if (S_ISDIR (src_stat.st_mode))
2463 value = move_dir_dir (source_with_path, dest);
2464 else
2465 value = move_file_file (source_with_path, dest);
2466 break;
2467 default:
2468 value = FILE_CONT;
2469 message_1s (1, _(" Internal failure "), _(" Unknown file operation "));
2470 }
2471 }
2472 } /* Copy or move operation */
2473
2474 if (value == FILE_CONT)
2475 unmark_files (panel);
2476
2477 } else {
2478 /* Many files */
2479
2480 if (operation != OP_DELETE){
2481 /* Check destination for copy or move operation */
2482 retry_many_dst_stat:
2483 dst_result = mc_stat (dest, &dst_stat);
2484 if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)){
2485 if (file_error (_(" Destination \"%s\" must be a directory \n %s "), dest) == FILE_RETRY)
2486 goto retry_many_dst_stat;
2487 goto clean_up;
2488 }
2489 }
2490
2491 /* Initialize variables for progress bars */
2492 marked = panel->marked;
2493 total = panel->total;
2494
2495 /* Loop for every file */
2496 for (i = 0; i < panel->count; i++){
2497 if (!panel->dir.list [i].f.marked)
2498 continue; /* Skip the unmarked ones */
2499 source = panel->dir.list [i].fname;
2500 src_stat = panel->dir.list [i].buf; /* Inefficient, should we use pointers? */
2501 #ifdef WITH_FULL_PATHS
2502 if (source_with_path)
2503 free (source_with_path);
2504 source_with_path = concat_dir_and_file (panel->cwd, source);
2505 #endif
2506 if (operation == OP_DELETE){
2507 /* Delete operation */
2508 if (S_ISDIR (src_stat.st_mode))
2509 value = erase_dir (source_with_path);
2510 else
2511 value = erase_file (source_with_path);
2512 } else {
2513 /* Copy or move operation */
2514 if (temp)
2515 free (temp);
2516 temp = transform_source (source_with_path);
2517 if (temp == NULL) {
2518 value = transform_error;
2519 } else {
2520 temp = get_full_name (dest, temp);
2521 switch (operation){
2522 case OP_COPY:
2523 /* we use op_follow_links only with OP_COPY,
2524 */
2525 (*xstat) (source_with_path, &src_stat);
2526 if (S_ISDIR (src_stat.st_mode))
2527 value = copy_dir_dir (source_with_path, temp, 1, 0, 0, 0);
2528 else
2529 value = copy_file_file (source_with_path, temp, 1);
2530 free_linklist (&dest_dirs);
2531 break;
2532 case OP_MOVE:
2533 if (S_ISDIR (src_stat.st_mode))
2534 value = move_dir_dir (source_with_path, temp);
2535 else
2536 value = move_file_file (source_with_path, temp);
2537 break;
2538 default:
2539 message_1s (1, _(" Internal failure "),
2540 _(" Unknown file operation "));
2541 goto clean_up;
2542 }
2543 }
2544 } /* Copy or move operation */
2545
2546 if (value == FILE_ABORT)
2547 goto clean_up;
2548 if (value == FILE_CONT){
2549 do_file_mark (panel, i, 0);
2550 }
2551 count ++;
2552 if (show_count_progress (count, marked) == FILE_ABORT)
2553 goto clean_up;
2554 bytes += src_stat.st_size;
2555 if (verbose &&
2556 show_bytes_progress (bytes, total) == FILE_ABORT)
2557 goto clean_up;
2558 if (operation != OP_DELETE && verbose
2559 && show_file_progress (0, 0) == FILE_ABORT)
2560 goto clean_up;
2561 mc_refresh ();
2562 } /* Loop for every file */
2563 } /* Many files */
2564
2565 clean_up:
2566 /* Clean up */
2567 destroy_op_win ();
2568 ftpfs_hint_reread (1);
2569 free_linklist (&linklist);
2570 free_linklist (&dest_dirs);
2571 #if WITH_FULL_PATHS
2572 if (source_with_path)
2573 free (source_with_path);
2574 #endif
2575 if (dest)
2576 free (dest);
2577 if (temp)
2578 free (temp);
2579 if (rx.buffer) {
2580 free (rx.buffer);
2581 rx.buffer = NULL;
2582 }
2583 if (dest_mask) {
2584 free (dest_mask);
2585 dest_mask = NULL;
2586 }
2587
2588 #ifdef WITH_BACKGROUND
2589 /* Let our parent know we are saying bye bye */
2590 if (we_are_background){
2591 vfs_shut ();
2592 tell_parent (MSG_CHILD_EXITING);
2593 exit (1);
2594 }
2595 #endif
2596 return 1;
2597 }
2598
2599 /* }}} */
2600
2601 /* {{{ Query/status report routines */
2602
2603 static int
2604 real_do_file_error (enum OperationMode mode, char *error)
2605 {
2606 int result;
2607 char *msg;
2608
2609 msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
2610 result = query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"), _("&Abort"));
2611
2612 switch (result){
2613 case 0:
2614 do_refresh ();
2615 return FILE_SKIP;
2616 case 1:
2617 do_refresh ();
2618 return FILE_RETRY;
2619 case 2:
2620 default:
2621 return FILE_ABORT;
2622 }
2623 }
2624
2625 /* Report error with one file */
2626 int
2627 file_error (char *format, char *file)
2628 {
2629 sprintf (cmd_buf, format,
2630 name_trunc (file, 30), unix_error_string (errno));
2631 return do_file_error (cmd_buf);
2632 }
2633
2634 /* Report error with two files */
2635 int
2636 files_error (char *format, char *file1, char *file2)
2637 {
2638 char nfile1 [16];
2639 char nfile2 [16];
2640
2641 strcpy (nfile1, name_trunc (file1, 15));
2642 strcpy (nfile2, name_trunc (file2, 15));
2643
2644 sprintf (cmd_buf, format, nfile1, nfile2, unix_error_string (errno));
2645 return do_file_error (cmd_buf);
2646 }
2647
2648 static char *format = N_("Target file \"%s\" already exists!");
2649 static int
2650 replace_callback (struct Dlg_head *h, int Id, int Msg)
2651 {
2652 #ifndef HAVE_X
2653
2654 switch (Msg){
2655 case DLG_DRAW:
2656 dialog_repaint (h, ERROR_COLOR, ERROR_COLOR);
2657 break;
2658 }
2659 #endif
2660 return 0;
2661 }
2662
2663 #ifdef HAVE_X
2664 #define X_TRUNC 128
2665 #else
2666 #define X_TRUNC 52
2667 #endif
2668
2669 /*
2670 * FIXME: probably it is better to replace this with quick dialog machinery,
2671 * but actually I'm not familiar with it and have not much time :(
2672 * alex
2673 */
2674 static struct
2675 {
2676 char* text;
2677 int ypos, xpos;
2678 int value; /* 0 for labels */
2679 char* tkname;
2680 WLay layout;
2681 }
2682 rd_widgets [] =
2683 {
2684 {N_("Target file \"%s\" already exists!"),
2685 3, 4, 0, "target-e", XV_WLAY_CENTERROW},
2686 {N_("&Abort"), BY + 3, 25, REPLACE_ABORT, "abort", XV_WLAY_CENTERROW},
2687 {N_("if &Size differs"),
2688 BY + 1, 28, REPLACE_SIZE, "if-size", XV_WLAY_RIGHTOF},
2689 {N_("non&E"), BY, 47, REPLACE_NEVER, "none", XV_WLAY_RIGHTOF},
2690 {N_("&Update"), BY, 36, REPLACE_UPDATE, "update", XV_WLAY_RIGHTOF},
2691 {N_("al&L"), BY, 28, REPLACE_ALWAYS, "all", XV_WLAY_RIGHTOF},
2692 {N_("Overwrite all targets?"),
2693 BY, 4, 0, "over-label", XV_WLAY_CENTERROW},
2694 {N_("&Reget"), BY - 1, 28, REPLACE_REGET, "reget", XV_WLAY_RIGHTOF},
2695 {N_("ap&Pend"), BY - 2, 45, REPLACE_APPEND, "append", XV_WLAY_RIGHTOF},
2696 {N_("&No"), BY - 2, 37, REPLACE_NO, "no", XV_WLAY_RIGHTOF},
2697 {N_("&Yes"), BY - 2, 28, REPLACE_YES, "yes", XV_WLAY_RIGHTOF},
2698 {N_("Overwrite this target?"),
2699 BY - 2, 4, 0, "overlab", XV_WLAY_CENTERROW},
2700 {N_("Target date: %s, size %d"),
2701 6, 4, 0, "target-date",XV_WLAY_CENTERROW},
2702 {N_("Source date: %s, size %d"),
2703 5, 4, 0, "source-date",XV_WLAY_CENTERROW}
2704 };
2705
2706 #define ADD_RD_BUTTON(i)\
2707 add_widgetl (replace_dlg,\
2708 button_new (rd_widgets [i].ypos, rd_widgets [i].xpos, rd_widgets [i].value,\
2709 NORMAL_BUTTON, rd_widgets [i].text, 0, 0, rd_widgets [i].tkname), \
2710 rd_widgets [i].layout)
2711
2712 #define ADD_RD_LABEL(i,p1,p2)\
2713 sprintf (buffer, rd_widgets [i].text, p1, p2);\
2714 add_widgetl (replace_dlg,\
2715 label_new (rd_widgets [i].ypos, rd_widgets [i].xpos, buffer, rd_widgets [i].tkname),\
2716 rd_widgets [i].layout)
2717
2718 static void
2719 init_replace (enum OperationMode mode)
2720 {
2721 char buffer [128];
2722 static int rd_xlen = 60, rd_trunc = X_TRUNC;
2723
2724 #ifdef ENABLE_NLS
2725 static int i18n_flag;
2726 if (!i18n_flag)
2727 {
2728 int l1, l2, l, row;
2729 register int i = sizeof (rd_widgets) / sizeof (rd_widgets [0]);
2730 while (i--)
2731 rd_widgets [i].text = _(rd_widgets [i].text);
2732
2733 /*
2734 *longest of "Overwrite..." labels
2735 * (assume "Target date..." are short enough)
2736 */
2737 l1 = max (strlen (rd_widgets [6].text), strlen (rd_widgets [11].text));
2738
2739 /* longest of button rows */
2740 i = sizeof (rd_widgets) / sizeof (rd_widgets [0]);
2741 for (row = l = l2 = 0; i--;)
2742 {
2743 if (rd_widgets [i].value != 0)
2744 {
2745 if (row != rd_widgets [i].ypos)
2746 {
2747 row = rd_widgets [i].ypos;
2748 l2 = max (l2, l);
2749 l = 0;
2750 }
2751 l += strlen (rd_widgets [i].text) + 4;
2752 }
2753 }
2754 l2 = max (l2, l); /* last row */
2755 rd_xlen = max (rd_xlen, l1 + l2 + 8);
2756 rd_trunc = rd_xlen - 6;
2757
2758 /* Now place buttons */
2759 l1 += 5; /* start of first button in the row */
2760 i = sizeof (rd_widgets) / sizeof (rd_widgets [0]);
2761
2762 for (l = l1, row = 0; --i > 1;)
2763 {
2764 if (rd_widgets [i].value != 0)
2765 {
2766 if (row != rd_widgets [i].ypos)
2767 {
2768 row = rd_widgets [i].ypos;
2769 l = l1;
2770 }
2771 rd_widgets [i].xpos = l;
2772 l += strlen (rd_widgets [i].text) + 4;
2773 }
2774 }
2775 /* Abort button is centered */
2776 rd_widgets [1].xpos = (rd_xlen - strlen (rd_widgets [1].text) - 3) / 2;
2777
2778 }
2779 #endif /* ENABLE_NLS */
2780
2781 replace_colors [0] = ERROR_COLOR;
2782 replace_colors [1] = COLOR_NORMAL;
2783 replace_colors [2] = ERROR_COLOR;
2784 replace_colors [3] = COLOR_NORMAL;
2785
2786 replace_dlg = create_dlg (0, 0, 16, rd_xlen, replace_colors, replace_callback,
2787 "[ Replace ]", "replace", DLG_CENTER);
2788
2789 x_set_dialog_title (replace_dlg,
2790 mode == Foreground ? _(" File exists ") : _(" Background process: File exists "));
2791
2792
2793 ADD_RD_LABEL(0, name_trunc (replace_filename, rd_trunc - strlen (rd_widgets [0].text)), 0 );
2794 ADD_RD_BUTTON(1);
2795
2796 tk_new_frame (replace_dlg, "a.");
2797
2798 ADD_RD_BUTTON(2);
2799 ADD_RD_BUTTON(3);
2800 ADD_RD_BUTTON(4);
2801 ADD_RD_BUTTON(5);
2802 ADD_RD_LABEL(6,0,0);
2803
2804 /* "this target..." widgets */
2805 tk_new_frame (replace_dlg, "p.");
2806 if (!S_ISDIR (d_stat->st_mode)){
2807 if ((do_reget == -1 && d_stat->st_size && s_stat->st_size > d_stat->st_size))
2808 ADD_RD_BUTTON(7);
2809
2810 ADD_RD_BUTTON(8);
2811 }
2812 ADD_RD_BUTTON(9);
2813 ADD_RD_BUTTON(10);
2814 ADD_RD_LABEL(11,0,0);
2815
2816 tk_new_frame (replace_dlg, "i.");
2817 ADD_RD_LABEL(12, file_date (d_stat->st_mtime), (int) d_stat->st_size);
2818 ADD_RD_LABEL(13, file_date (s_stat->st_mtime), (int) s_stat->st_size);
2819 tk_end_frame ();
2820 }
2821
2822 static int
2823 real_query_replace (enum OperationMode mode, char *destname, struct stat *_s_stat,
2824 struct stat *_d_stat)
2825 {
2826 if (replace_result < REPLACE_ALWAYS){
2827 replace_filename = destname;
2828 s_stat = _s_stat;
2829 d_stat = _d_stat;
2830 init_replace (mode);
2831 run_dlg (replace_dlg);
2832 replace_result = replace_dlg->ret_value;
2833 if (replace_result == B_CANCEL)
2834 replace_result = REPLACE_ABORT;
2835 destroy_dlg (replace_dlg);
2836 }
2837
2838 switch (replace_result){
2839 case REPLACE_UPDATE:
2840 do_refresh ();
2841 if (_s_stat->st_mtime > _d_stat->st_mtime)
2842 return FILE_CONT;
2843 else
2844 return FILE_SKIP;
2845
2846 case REPLACE_SIZE:
2847 do_refresh ();
2848 if (_s_stat->st_size == _d_stat->st_size)
2849 return FILE_SKIP;
2850 else
2851 return FILE_CONT;
2852
2853 case REPLACE_REGET:
2854 do_reget = _d_stat->st_size;
2855
2856 case REPLACE_APPEND:
2857 do_append = 1;
2858
2859 case REPLACE_YES:
2860 case REPLACE_ALWAYS:
2861 do_refresh ();
2862 return FILE_CONT;
2863 case REPLACE_NO:
2864 case REPLACE_NEVER:
2865 do_refresh ();
2866 return FILE_SKIP;
2867 case REPLACE_ABORT:
2868 default:
2869 return FILE_ABORT;
2870 }
2871 }
2872
2873 int
2874 real_query_recursive (enum OperationMode mode, char *s)
2875 {
2876 char *confirm, *text;
2877
2878 if (recursive_result < RECURSIVE_ALWAYS){
2879 char *msg =
2880 mode == Foreground ? _("\n Directory not empty. \n Delete it recursively? ")
2881 : _("\n Background process: Directory not empty \n Delete it recursively? ");
2882 text = copy_strings (" Delete: ", name_trunc (s, 30), " ", 0);
2883
2884 if (know_not_what_am_i_doing)
2885 query_set_sel (1);
2886 recursive_result = query_dialog (text, msg, D_ERROR, 5,
2887 _("&Yes"), _("&No"), _("a&ll"), _("non&E"), _("&Abort"));
2888
2889
2890 if (recursive_result != RECURSIVE_ABORT)
2891 do_refresh ();
2892 free (text);
2893 if (know_not_what_am_i_doing && (recursive_result == RECURSIVE_YES
2894 || recursive_result == RECURSIVE_ALWAYS)){
2895 text = copy_strings (_(" Type 'yes' if you REALLY want to delete "),
2896 recursive_result == RECURSIVE_YES
2897 ? name_trunc (s, 19) : _("all the directories "), " ", 0);
2898 confirm = input_dialog (mode == Foreground ? _(" Recursive Delete ")
2899 : _(" Background process: Recursive Delete "),
2900 text, "no");
2901 do_refresh ();
2902 if (!confirm || strcmp (confirm, "yes"))
2903 recursive_result = RECURSIVE_NEVER;
2904 free (confirm);
2905 free (text);
2906 }
2907 }
2908 switch (recursive_result){
2909 case RECURSIVE_YES:
2910 case RECURSIVE_ALWAYS:
2911 return FILE_CONT;
2912 case RECURSIVE_NO:
2913 case RECURSIVE_NEVER:
2914 return FILE_SKIP;
2915 case RECURSIVE_ABORT:
2916 default:
2917 return FILE_ABORT;
2918 }
2919 }
2920
2921 #ifdef WITH_BACKGROUND
2922 int
2923 do_file_error (char *str)
2924 {
2925 return call_1s (real_do_file_error, str);
2926 }
2927
2928 int
2929 query_recursive (char *s)
2930 {
2931 return call_1s (real_query_recursive, s);
2932 }
2933
2934 int
2935 query_replace (char *destname, struct stat *_s_stat, struct stat *_d_stat)
2936 {
2937 if (we_are_background)
2938 return parent_call ((void *)real_query_replace, 3, strlen(destname), destname,
2939 sizeof (struct stat), _s_stat, sizeof(struct stat), _d_stat);
2940 else
2941 return real_query_replace (Foreground, destname, _s_stat, _d_stat);
2942 }
2943
2944 #else
2945 do_file_error (char *str)
2946 {
2947 return real_do_file_error (Foreground, str);
2948 }
2949
2950 int
2951 query_recursive (char *s)
2952 {
2953 return real_query_recursive (Foreground, s);
2954 }
2955
2956 int
2957 query_replace (char *destname, struct stat *_s_stat, struct stat *_d_stat)
2958 {
2959 return real_query_replace (Foreground, destname, _s_stat, _d_stat);
2960 }
2961
2962 #endif
2963
2964 /*
2965 Cause emacs to enter folding mode for this file:
2966 Local variables:
2967 end:
2968 */