home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / mc454src.zip / mc-4.5.4.src / mc-4.5.4 / src / file.c < prev    next >
C/C++ Source or Header  |  1999-01-04  |  63KB  |  2,392 lines

  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. #include <time.h>
  83. #include <utime.h>
  84. #include "mad.h"
  85. #include "regex.h"
  86. #include "util.h"
  87. #include "dialog.h"
  88. #include "global.h"
  89. #include "setup.h"
  90. /* Needed by query_replace */
  91. #include "color.h"
  92. #include "win.h"
  93. #include "dlg.h"
  94. #include "widget.h"
  95. #define WANT_WIDGETS
  96. #include "main.h"        /* WANT_WIDGETS-> we get the the_hint def */
  97. #include "background.h"
  98. #include "layout.h"
  99. #include "widget.h"
  100. #include "wtools.h"
  101.  
  102. /* Needed for current_panel, other_panel and WTree */
  103. #include "dir.h"
  104. #include "panel.h"
  105. #include "file.h"
  106. #include "filegui.h"
  107. #include "tree.h"
  108. #include "key.h"
  109. #include "../vfs/vfs.h"
  110.  
  111. #include "x.h"
  112.  
  113. /* }}} */
  114.  
  115. /* rcsid [] = "$Id: file.c,v 1.37 1998/12/30 02:51:11 unammx Exp $" */
  116. int verbose = 1;
  117.  
  118. /* Recursive operation on subdirectories */
  119. int dive_into_subdirs = 0;
  120.  
  121. /*
  122.  * When moving directories cross filesystem boundaries delete the successfull
  123.  * copied files when all files below the directory and its subdirectories 
  124.  * were processed. 
  125.  * If erase_at_end is zero files will be deleted immediately after their
  126.  * successful copy (Note: this behaviour is not tested and at the moment
  127.  * it can't be changed at runtime)
  128.  */
  129. int erase_at_end = 1;
  130.  
  131. /*
  132.  * Preserve the original files' owner, group, permissions, and
  133.  * timestamps (owner, group only as root).
  134.  */
  135. int file_mask_preserve;
  136.  
  137. /*
  138.  * Whether the Midnight Commander tries to provide more
  139.  * information about copy/move sizes and bytes transfered
  140.  * at the expense of some speed
  141.  */
  142. int file_op_compute_totals = 1;
  143.  
  144. /*
  145.  * If running as root, preserve the original uid/gid
  146.  * (we don't want to try chwon for non root)
  147.  * preserve_uidgid = preserve && uid == 0 */
  148. int file_mask_preserve_uidgid = 0;
  149.  
  150. /* The bits to preserve in created files' modes on file copy */
  151. int file_mask_umask_kill = 0777777;
  152.  
  153. /* If on, it gets a little scrict with dangerous operations */
  154. int know_not_what_am_i_doing = 0;
  155.  
  156. int file_mask_stable_symlinks = 0;
  157.  
  158. /* The next two are not static, since they are used on background.c */
  159. /* Controls appending to files, shared with filequery.c */
  160. int file_progress_do_append = 0;
  161.  
  162. /* result from the recursive query */
  163. int file_progress_recursive_result;
  164.  
  165. /* The estimated time of arrival in seconds */
  166. double file_progress_eta_secs;
  167.  
  168. /* The reget flag */
  169. int file_progress_do_reget = 1;
  170.  
  171. /* Status reporting flags */
  172. int file_progress_totals_computed; /* panel total has been computed */
  173. long file_progress_count;
  174. double file_progress_bytes;
  175.  
  176. /* mapping operations into names */
  177. char *operation_names [] = { "Copy", "Move", "Delete" };
  178.  
  179. /* This is a hard link cache */
  180. struct link {
  181.     struct link *next;
  182.     vfs *vfs;
  183.     dev_t dev;
  184.     ino_t ino;
  185.     short linkcount;
  186.     umode_t st_mode;
  187.     char name[1];
  188. };
  189.  
  190. /* the hard link cache */
  191. struct link *linklist = NULL;
  192.  
  193. /* the files-to-be-erased list */
  194. struct link *erase_list;
  195.  
  196. /*
  197.  * In copy_dir_dir we use two additional single linked lists: The first - 
  198.  * variable name `parent_dirs' - holds information about already copied 
  199.  * directories and is used to detect cyclic symbolic links. 
  200.  * The second (`dest_dirs' below) holds information about just created 
  201.  * target directories and is used to detect when an directory is copied 
  202.  * into itself (we don't want to copy infinitly). 
  203.  * Both lists don't use the linkcount and name structure members of struct
  204.  * link.
  205.  */
  206. struct link *dest_dirs = 0;
  207.  
  208. struct re_pattern_buffer file_mask_rx;
  209. struct re_registers regs;
  210.  
  211. char *file_mask_dest_mask = NULL;
  212.  
  213. /*
  214.  * To symlinks the difference between `follow Links' checked and not
  215.  * checked is the stat call used (mc_stat resp. mc_lstat)
  216.  */
  217. int (*file_mask_xstat)(char *, struct stat *) = mc_lstat;
  218.  
  219. int   file_mask_op_follow_links = 0;
  220.  
  221. #ifndef HAVE_GNOME
  222. /* we don't need these in the GNOME version */
  223. char *file_progress_replace_filename;
  224. int   file_progress_replace_result;
  225. #endif
  226. unsigned long file_progress_bps = 0, file_progress_bps_time = 0;
  227.  
  228. char *op_names [3] = {
  229.     N_(" Copy "),
  230.     N_(" Move "),
  231.     N_(" Delete ")
  232. };
  233.  
  234. static int recursive_erase (char *s, long *progress_count, double *progress_bytes);
  235.  
  236. /* }}} */
  237.  
  238.  
  239. enum CaseConvs { NO_CONV=0, UP_CHAR=1, LOW_CHAR=2, UP_SECT=4, LOW_SECT=8 };
  240.  
  241. static int
  242. convert_case (int c, enum CaseConvs *conversion)
  243. {
  244.     if (*conversion & UP_CHAR){
  245.     *conversion &= ~UP_CHAR;
  246.     return toupper (c);
  247.     } else if (*conversion & LOW_CHAR){
  248.     *conversion &= ~LOW_CHAR;
  249.     return tolower (c);
  250.     } else if (*conversion & UP_SECT){
  251.     return toupper (c);
  252.     } else if (*conversion & LOW_SECT){
  253.     return tolower (c);
  254.     } else
  255.     return c;
  256. }
  257.  
  258. static int transform_error = 0;
  259.  
  260. static char *
  261. do_transform_source (char *source)
  262. {
  263.     int j, k, l, len;
  264.     char *fnsource = x_basename (source);
  265.     int next_reg;
  266.     enum CaseConvs case_conv = NO_CONV;
  267.     static char fntarget [MC_MAXPATHLEN];
  268.     
  269.     len = strlen (fnsource);
  270.     j = re_match (&file_mask_rx, fnsource, len, 0, ®s);
  271.     if (j != len){
  272.         transform_error = FILE_SKIP;
  273.         return NULL;
  274.     }
  275.     for (next_reg = 1, j = 0, k = 0; j < strlen (file_mask_dest_mask); j++){
  276.         switch (file_mask_dest_mask [j]){
  277.     case '\\':
  278.         j++;
  279.         if (! isdigit (file_mask_dest_mask [j])){
  280.         /* Backslash followed by non-digit */
  281.         switch (file_mask_dest_mask [j]){
  282.         case 'U':
  283.             case_conv |= UP_SECT;
  284.             case_conv &= ~LOW_SECT;
  285.             break;
  286.         case 'u':
  287.             case_conv |= UP_CHAR;
  288.             break;
  289.         case 'L':
  290.             case_conv |= LOW_SECT;
  291.             case_conv &= ~UP_SECT;
  292.             break;
  293.         case 'l':
  294.             case_conv |= LOW_CHAR;
  295.             break;
  296.         case 'E':
  297.             case_conv = NO_CONV;
  298.             break;
  299.         default:
  300.             /* Backslash as quote mark */
  301.             fntarget [k++] = convert_case (file_mask_dest_mask [j], &case_conv);
  302.         }
  303.         break;
  304.         } else {
  305.         /* Backslash followed by digit */
  306.         next_reg = file_mask_dest_mask [j] - '0';
  307.         /* Fall through */
  308.         }
  309.                 
  310.     case '*':
  311.         if (next_reg < 0 || next_reg >= RE_NREGS
  312.         || regs.start [next_reg] < 0){
  313.         message_1s (1, MSG_ERROR, _(" Invalid target mask "));
  314.         transform_error = FILE_ABORT;
  315.         return NULL;
  316.         }
  317.         for (l = regs.start [next_reg]; l < regs.end [next_reg]; l++)
  318.         fntarget [k++] = convert_case (fnsource [l], &case_conv);
  319.         next_reg ++;
  320.         break;
  321.                 
  322.     default:
  323.         fntarget [k++] = convert_case (file_mask_dest_mask [j], &case_conv);
  324.         break;
  325.         }
  326.     }
  327.     fntarget [k] = 0;
  328.     return fntarget;
  329. }
  330.  
  331. static char *
  332. transform_source (char *source)
  333. {
  334.     char *s = strdup (source);
  335.     char *q;
  336.  
  337.     /* We remove \n from the filename since regex routines would use \n as an anchor */
  338.     /* this is just to be allowed to maniupulate file names with \n on it */
  339.     for (q = s; *q; q++){
  340.     if (*q == '\n')
  341.         *q = ' ';
  342.     }
  343.     q = do_transform_source (s);
  344.     free (s);
  345.     return q;
  346. }
  347.  
  348. static void
  349. free_linklist (struct link **linklist)
  350. {
  351.     struct link *lp, *lp2;
  352.     
  353.     for (lp = *linklist; lp != NULL; lp = lp2){
  354.         lp2 = lp -> next;
  355.         free (lp);
  356.     }
  357.     *linklist = NULL;
  358. }
  359.  
  360. static int 
  361. is_in_linklist (struct link *lp, char *path, struct stat *sb)
  362. {
  363.    ino_t ino = sb->st_ino;
  364.    dev_t dev = sb->st_dev;
  365. #ifdef USE_VFS
  366.    vfs *vfs = vfs_type (path);
  367. #endif
  368.    
  369.    while (lp){
  370. #ifdef USE_VFS
  371.       if (lp->vfs == vfs)
  372. #endif
  373.       if (lp->ino == ino && lp->dev == dev )
  374.           return 1;
  375.       lp = lp->next;
  376.    }
  377.    return 0;
  378. }
  379.  
  380. /*
  381.  * Returns 0 if the inode wasn't found in the cache and 1 if it was found
  382.  * and a hardlink was succesfully made
  383.  */
  384. static int
  385. check_hardlinks (char *src_name, char *dst_name, struct stat *pstat)
  386. {
  387.     struct link *lp;
  388.     vfs *my_vfs = vfs_type (src_name);
  389.     ino_t ino = pstat->st_ino;
  390.     dev_t dev = pstat->st_dev;
  391.     struct stat link_stat;
  392.     char *p;
  393.  
  394. #if 1    /* What will happen if we kill this line? mc_link() will fail on this and it is right behaviour... */
  395.     if (vfs_file_is_ftp (src_name))
  396.         return 0;
  397. #endif
  398.     for (lp = linklist; lp != NULL; lp = lp -> next)
  399.         if (lp->vfs == my_vfs && lp->ino == ino && lp->dev == dev){
  400.             if (!mc_stat (lp->name, &link_stat) && link_stat.st_ino == ino &&
  401.                 link_stat.st_dev == dev && vfs_type (lp->name) == my_vfs){
  402.                 p = strchr (lp->name, 0) + 1; /* i.e. where the `name' file
  403.                                      was copied to */
  404.                 if (vfs_type (dst_name) == vfs_type (p)){
  405.                     if (!mc_stat (p, &link_stat)){
  406.                         if (!mc_link (p, dst_name))
  407.                             return 1;
  408.                     }
  409.                 }
  410.             }
  411.         message_1s (1, MSG_ERROR, _(" Could not make the hardlink "));
  412.             return 0;
  413.         }
  414.     lp = (struct link *) xmalloc (sizeof (struct link) + strlen (src_name) 
  415.                                   + strlen (dst_name) + 1, "Hardlink cache");
  416.     if (lp){
  417.         lp->vfs = my_vfs;
  418.         lp->ino = ino;
  419.         lp->dev = dev;
  420.         strcpy (lp->name, src_name);
  421.         p = strchr (lp->name, 0) + 1;
  422.         strcpy (p, dst_name);
  423.         lp->next = linklist;
  424.         linklist = lp;
  425.     }
  426.     return 0;
  427. }
  428.  
  429. /*
  430.  * Duplicate the contents of the symbolic link src_path in dst_path.
  431.  * Try to make a stable symlink if the option "stable symlink" was
  432.  * set in the file mask dialog.
  433.  * If dst_path is an existing symlink it will be deleted silently
  434.  * (upper levels take already care of existing files at dst_path).
  435.  */
  436. static int 
  437. make_symlink (char *src_path, char *dst_path)
  438. {
  439.     char link_target[MC_MAXPATHLEN];
  440.     int len;
  441.     int return_status;
  442.     struct stat sb;
  443.     int dst_is_symlink;
  444.     
  445.     if (mc_lstat (dst_path, &sb) == 0 && S_ISLNK (sb.st_mode)) 
  446.     dst_is_symlink = 1;
  447.     else
  448.     dst_is_symlink = 0;
  449.  
  450.   retry_src_readlink:
  451.     len = mc_readlink (src_path, link_target, MC_MAXPATHLEN);
  452.     if (len < 0){
  453.     return_status = file_error
  454.         (_(" Cannot read source link \"%s\" \n %s "), src_path);
  455.     if (return_status == FILE_RETRY)
  456.         goto retry_src_readlink;
  457.     return return_status;
  458.     }
  459.     link_target[len] = 0;
  460.  
  461.     if (file_mask_stable_symlinks)
  462.     if ((!vfs_file_is_local (src_path)||!vfs_file_is_local (dst_path))){
  463.         message_1s (1, MSG_ERROR, _(
  464.         " Cannot make stable symlinks across "
  465.         "non-local filesystems: \n\n"
  466.         " Option Stable Symlinks will be disabled "));
  467.         file_mask_stable_symlinks = 0;
  468.     }
  469.     
  470.     if (file_mask_stable_symlinks && *link_target != PATH_SEP){
  471.     char *p, *q, *r, *s;
  472.  
  473.     p = strdup (src_path);
  474.     r = strrchr (p, PATH_SEP);
  475.  
  476.     if (r){
  477.         r[1] = 0;
  478.         if (*dst_path == PATH_SEP)
  479.         q = strdup (dst_path);
  480.         else
  481.         q = copy_strings (p, dst_path, 0);
  482.         r = strrchr (q, PATH_SEP);
  483.         if (r){
  484.         r[1] = 0;
  485.         s = copy_strings (p, link_target, NULL);
  486.         strcpy (link_target, s);
  487.         free (s);
  488.         s = diff_two_paths (q, link_target);
  489.         if (s){
  490.             strcpy (link_target, s);
  491.             free (s);
  492.         }
  493.         }
  494.         free (q);
  495.     }
  496.     free (p);
  497.     }
  498.   retry_dst_symlink:
  499.     if (mc_symlink (link_target, dst_path) == 0)
  500.     /* Success */
  501.     return FILE_CONT;
  502.     /*
  503.      * if dst_exists, it is obvious that this had failed.
  504.      * We can delete the old symlink and try again...
  505.      */
  506.     if (dst_is_symlink){
  507.     if (!mc_unlink (dst_path))
  508.         if (mc_symlink (link_target, dst_path) == 0)
  509.         /* Success */
  510.         return FILE_CONT;
  511.     }
  512.     return_status = file_error
  513.     (_(" Cannot create target symlink \"%s\" \n %s "), dst_path);
  514.     if (return_status == FILE_RETRY)
  515.     goto retry_dst_symlink;
  516.     return return_status;
  517. }
  518.  
  519. static int
  520. progress_update_one (long *progress_count, 
  521.                      double *progress_bytes, 
  522.                      int add,
  523.                      int is_toplevel_file)
  524. {
  525.     int ret;
  526.  
  527.     if (is_toplevel_file || file_progress_totals_computed) {
  528.         (*progress_count)++;
  529.         (*progress_bytes) += add;
  530.     }
  531.  
  532.     /* Apply some heuristic here to not call the update stuff very often */
  533.     ret = file_progress_show_count (*progress_count, file_progress_count);
  534.  
  535.     if (ret != FILE_CONT)
  536.         return ret;
  537.     
  538.     ret = file_progress_show_bytes (*progress_bytes, file_progress_bytes);
  539.  
  540.     return ret;
  541. }
  542.  
  543. int
  544. copy_file_file (char *src_path, char *dst_path, int ask_overwrite,
  545.         long  *progress_count, double *progress_bytes, 
  546.                 int is_toplevel_file)
  547. {
  548. #ifndef OS2_NT
  549.     uid_t src_uid;
  550.     gid_t src_gid;
  551. #endif
  552.     char *buf = 0;
  553.     int  buf_size = 8*1024;
  554.     int  src_desc, dest_desc = 0;
  555.     int  n_read, n_written;
  556.     int  src_mode;        /* The mode of the source file */
  557.     struct stat sb, sb2;
  558.     struct utimbuf utb;
  559.     int  dst_exists = 0, appending = 0;
  560.     long n_read_total = 0, file_size;
  561.     int  return_status, temp_status;
  562.     struct timeval tv_transfer_start;
  563.  
  564.     /* bitmask used to remember which resourses we should release on return 
  565.        A single goto label is much easier to handle than a bunch of gotos ;-). */ 
  566.     unsigned resources = 0; 
  567.  
  568.     /* FIXME: We should not be using global variables! */
  569.     file_progress_do_reget = 0; 
  570.     return_status = FILE_RETRY;
  571.  
  572.     if (file_progress_show_source (src_path) == FILE_ABORT ||
  573.     file_progress_show_target (dst_path) == FILE_ABORT)
  574.     return FILE_ABORT;
  575.     mc_refresh ();
  576.  
  577.  retry_dst_stat:
  578.     if (mc_stat (dst_path, &sb2) == 0){
  579.     if (S_ISDIR (sb2.st_mode)){
  580.         return_status = file_error (_(" Cannot overwrite directory \"%s\" \n %s "),
  581.                     dst_path);
  582.         if (return_status == FILE_RETRY)
  583.         goto retry_dst_stat;
  584.         return return_status;
  585.     }
  586.     dst_exists = 1;
  587.     }
  588.  
  589.     while ((*file_mask_xstat)(src_path, &sb)){
  590.     return_status = file_error (_(" Cannot stat source file \"%s\" \n %s "),
  591.                     src_path);
  592.     if (return_status == FILE_RETRY)
  593.         continue;
  594.     return return_status;
  595.     } 
  596.     
  597.     if (dst_exists){
  598.         /* .ado: For OS/2 or NT: no st_ino exists, it is better to just try to
  599.          * overwrite the target file
  600.          */
  601. #ifndef OS2_NT
  602.     /* Destination already exists */
  603.     if (sb.st_dev == sb2.st_dev && sb.st_ino == sb2.st_ino){
  604.         message_3s (1, MSG_ERROR, _(" `%s' and `%s' are the same file. "),
  605.              src_path, dst_path);
  606.         do_refresh ();
  607.         return FILE_SKIP;
  608.     }
  609. #endif
  610.  
  611.     /* Should we replace destination? */
  612.     if (ask_overwrite){
  613.         file_progress_do_reget = 0;
  614.             
  615.         return_status = query_replace (dst_path, &sb, &sb2);
  616.         if (return_status != FILE_CONT)
  617.             return return_status;
  618.     }
  619.     }
  620.  
  621.     if (!file_progress_do_append){
  622.        /* .ado: OS2 and NT don't have hardlinks */
  623. #ifndef OS2_NT    
  624.         /* Check the hardlinks */
  625.         if (!file_mask_op_follow_links && sb.st_nlink > 1 && 
  626.          check_hardlinks (src_path, dst_path, &sb) == 1){
  627.             /* We have made a hardlink - no more processing is necessary */
  628.             return return_status;
  629.         }
  630.     
  631.         if (S_ISLNK (sb.st_mode))
  632.         return make_symlink (src_path, dst_path);
  633.         
  634. #endif /* !OS_NT */
  635.  
  636.         if (S_ISCHR (sb.st_mode) || S_ISBLK (sb.st_mode) || S_ISFIFO (sb.st_mode)
  637.             || S_ISSOCK (sb.st_mode)){
  638.             while (mc_mknod (dst_path, sb.st_mode & file_mask_umask_kill, sb.st_rdev) < 0){
  639.             return_status = file_error
  640.             (_(" Cannot create special file \"%s\" \n %s "), dst_path);
  641.             if (return_status == FILE_RETRY)
  642.             continue;
  643.         return return_status;
  644.         }
  645.         /* Success */
  646.         
  647. #ifndef OS2_NT
  648.         while (file_mask_preserve_uidgid && mc_chown (dst_path, sb.st_uid, sb.st_gid)){
  649.         temp_status = file_error
  650.             (_(" Cannot chown target file \"%s\" \n %s "), dst_path);
  651.         if (temp_status == FILE_RETRY)
  652.             continue;
  653.         return temp_status;
  654.         }
  655. #endif
  656. #ifndef __os2__
  657.         while (file_mask_preserve &&
  658.         (mc_chmod (dst_path, sb.st_mode & file_mask_umask_kill) < 0)){
  659.         temp_status = file_error (_(" Cannot chmod target file \"%s\" \n %s "), dst_path);
  660.         if (temp_status == FILE_RETRY)
  661.             continue;
  662.         return temp_status;
  663.         }
  664. #endif
  665.         return FILE_CONT;
  666.         }
  667.     }
  668.     
  669.     gettimeofday (&tv_transfer_start, (struct timezone *) NULL);
  670.  
  671.     while ((src_desc = mc_open (src_path, O_RDONLY | O_LINEAR)) < 0){
  672.     return_status = file_error
  673.         (_(" Cannot open source file \"%s\" \n %s "), src_path);
  674.     if (return_status == FILE_RETRY)
  675.         continue;
  676.     file_progress_do_append = 0;
  677.     return return_status;
  678.     }
  679.  
  680.     resources |= 1;
  681.     if (file_progress_do_reget){
  682.         if (mc_lseek (src_desc, file_progress_do_reget, SEEK_SET) != file_progress_do_reget){
  683.         message_1s (1, _(" Warning "), _(" Reget failed, about to overwrite file "));
  684.         file_progress_do_reget = file_progress_do_append = 0;
  685.     }
  686.     }
  687.     
  688.     while (mc_fstat (src_desc, &sb)){
  689.         return_status = file_error
  690.         (_(" Cannot fstat source file \"%s\" \n %s "), src_path);
  691.     if (return_status == FILE_RETRY)
  692.         continue;
  693.     file_progress_do_append = 0;
  694.     goto ret;
  695.     }
  696.     src_mode = sb.st_mode;
  697. #ifndef OS2_NT
  698.     src_uid = sb.st_uid;
  699.     src_gid = sb.st_gid;
  700. #endif
  701.     utb.actime = sb.st_atime;
  702.     utb.modtime = sb.st_mtime;
  703.     file_size = sb.st_size;
  704.  
  705.     /* Create the new regular file with small permissions initially,
  706.        do not create a security hole.  FIXME: You have security hole
  707.        here, btw. Imagine copying to /tmp and symlink attack :-( */
  708.  
  709.     while ((dest_desc = mc_open (dst_path, O_WRONLY | 
  710.       (file_progress_do_append ? O_APPEND : (O_CREAT | O_TRUNC)), 0600)) < 0){
  711.         return_status = file_error
  712.         (_(" Cannot create target file \"%s\" \n %s "), dst_path);
  713.     if (return_status == FILE_RETRY)
  714.         continue;
  715.     file_progress_do_append = 0;
  716.     goto ret;
  717.     }
  718.     resources |= 2; /* dst_path exists/dst_path opened */
  719.     resources |= 4; /* remove short file */
  720.  
  721.     appending = file_progress_do_append;
  722.     file_progress_do_append = 0;
  723.  
  724.     /* Find out the optimal buffer size.  */
  725.     while (mc_fstat (dest_desc, &sb)){
  726.         return_status = file_error
  727.         (_(" Cannot fstat target file \"%s\" \n %s "), dst_path);
  728.     if (return_status == FILE_RETRY)
  729.         continue;
  730.     goto ret;
  731.     }
  732.     buf = (char *) xmalloc (buf_size, "copy_file_file");
  733.  
  734.     file_progress_eta_secs = 0.0;
  735.     file_progress_bps = 0;
  736.  
  737.     return_status = file_progress_show (0, file_size);
  738.  
  739.     mc_refresh ();
  740.  
  741.     if (return_status != FILE_CONT)
  742.     goto ret;
  743.  
  744.     {
  745.     struct timeval tv_current, tv_last_update, tv_last_input;
  746.         int    secs, update_secs;
  747.     long   dt;
  748.     char   *stalled_msg;
  749.  
  750.     tv_last_update = tv_transfer_start;
  751.       
  752.         for (;;){
  753.  /* src_read */
  754.         if (mc_ctl (src_desc, MCCTL_IS_NOTREADY, 0))
  755.             n_read = -1;
  756.         else
  757.             while ((n_read = mc_read (src_desc, buf, buf_size))<0){
  758.             return_status = file_error(_(" Cannot read source file \"%s\" \n %s "), src_path);
  759.             if (return_status == FILE_RETRY)
  760.                 continue;
  761.             goto ret;
  762.         }
  763.         if (n_read == 0)
  764.             break;
  765.  
  766.         gettimeofday (&tv_current, NULL);
  767.  
  768.         if (n_read>0){
  769.             n_read_total += n_read;
  770.  
  771.         /* Windows NT ftp servers report that files have no
  772.          * permissions: -------, so if we happen to have actually
  773.          * read something, we should fix the permissions.
  774.          */
  775.         if (!(src_mode &
  776.               ((S_IRUSR|S_IWUSR|S_IXUSR)    /* user */
  777.                |(S_IXOTH|S_IWOTH|S_IROTH)  /* other */
  778.                |(S_IXGRP|S_IWGRP|S_IRGRP)))) /* group */
  779.             src_mode = S_IRUSR|S_IWUSR|S_IROTH|S_IRGRP;
  780.         gettimeofday (&tv_last_input, NULL);
  781.  
  782.  /* dst_write */
  783.         while ((n_written = mc_write (dest_desc, buf, n_read)) < n_read){
  784.             if (n_written>0){
  785.                 n_read -= n_written;
  786.             continue;
  787.             }
  788.             return_status = file_error(_(" Cannot write target file \"%s\" \n %s "), dst_path);
  789.             if (return_status == FILE_RETRY)
  790.                 continue;
  791.             goto ret;
  792.         }
  793.         }
  794.  
  795.         /* 1. Update rotating dash after some time (hardcoded to 2 seconds) */
  796.         secs = (tv_current.tv_sec - tv_last_update.tv_sec);
  797.         if (secs > 2){
  798.         rotate_dash ();
  799.         tv_last_update = tv_current;
  800.         }
  801.  
  802.         /* 2. Check for a stalled condition */
  803.         update_secs = (tv_current.tv_sec - tv_last_input.tv_sec);
  804.         stalled_msg = "";
  805.         if (update_secs > 4){
  806.         stalled_msg = _("(stalled)");
  807.         }
  808.  
  809.         /* 3. Compute ETA */
  810.         if (secs > 2){
  811.         dt = (tv_current.tv_sec - tv_transfer_start.tv_sec);
  812.         
  813.         if (n_read_total){
  814.             file_progress_eta_secs = ((dt / (double) n_read_total) * file_size) - dt;
  815.             file_progress_bps = n_read_total / ((dt < 1) ? 1 : dt);
  816.         } else
  817.             file_progress_eta_secs = 0.0;
  818.         }
  819.  
  820.         /* 4. Compute BPS rate */
  821.         if (secs > 2){
  822.         file_progress_bps_time = (tv_current.tv_sec - tv_transfer_start.tv_sec);
  823.         if (file_progress_bps_time < 1)
  824.             file_progress_bps_time = 1;
  825.         file_progress_bps = n_read_total / file_progress_bps_time;
  826.         }
  827.  
  828.         file_progress_set_stalled_label (stalled_msg);
  829.  
  830.         return_status = file_progress_show (n_read_total, file_size);
  831.         mc_refresh ();
  832.         if (return_status != FILE_CONT)
  833.             goto ret;
  834.         }
  835.     }
  836.    
  837.     resources &= ~4; /* copy successful, don't remove target file */
  838.  
  839. ret:
  840.     if (buf)
  841.     free (buf);
  842.     
  843.     while ((resources & 1) && mc_close (src_desc) < 0){
  844.     temp_status = file_error
  845.         (_(" Cannot close source file \"%s\" \n %s "), src_path);
  846.     if (temp_status == FILE_RETRY)
  847.         continue;
  848.     if (temp_status == FILE_ABORT)
  849.         return_status = temp_status;
  850.     break;
  851.     }
  852.  
  853.     while ((resources & 2) && mc_close (dest_desc) < 0){
  854.     temp_status = file_error
  855.         (_(" Cannot close target file \"%s\" \n %s "), dst_path);
  856.     if (temp_status == FILE_RETRY)
  857.         continue;
  858.     return_status = temp_status;
  859.     break;
  860.     }
  861.     
  862.     if (resources & 4){
  863.         /* Remove short file */
  864.         int result;
  865.         result = query_dialog ("Copy", _("Incomplete file was retrieved. Keep it?"), D_ERROR, 2, _("&Delete"), _("&Keep"));
  866.     if (!result)
  867.         mc_unlink (dst_path);
  868.     } else if (resources & (2|8)){
  869.         /* no short file and destination file exists */
  870. #ifndef OS2_NT 
  871.     if (!appending && file_mask_preserve_uidgid){
  872.             while (mc_chown (dst_path, src_uid, src_gid)){
  873.         temp_status = file_error
  874.                 (_(" Cannot chown target file \"%s\" \n %s "), dst_path);
  875.         if (temp_status == FILE_RETRY)
  876.                 continue;
  877.         return_status = temp_status;
  878.         break;
  879.             }
  880.     }
  881. #endif
  882.  
  883.      /*
  884.       * .ado: according to the XPG4 standard, the file must be closed before
  885.       * chmod can be invoked
  886.       */
  887.      retry_dst_chmod:
  888.     if (!appending && mc_chmod (dst_path, src_mode & file_mask_umask_kill)){
  889.         temp_status = file_error
  890.         (_(" Cannot chmod target file \"%s\" \n %s "), dst_path);
  891.         if (temp_status == FILE_RETRY)
  892.         goto retry_dst_chmod;
  893.         return_status = temp_status;
  894.     }
  895.     
  896.     if (!appending && file_mask_preserve)
  897.         mc_utime (dst_path, &utb);
  898.     }
  899.  
  900.     if (return_status == FILE_CONT)
  901.         return_status = progress_update_one (
  902.                                 progress_count, 
  903.                                 progress_bytes, 
  904.                                 file_size, is_toplevel_file);
  905.     
  906.     return return_status;
  907. }
  908.  
  909. /*
  910.  * I think these copy_*_* functions should have a return type.
  911.  * anyway, this function *must* have two directories as arguments.
  912.  */
  913. /* FIXME: This function needs to check the return values of the
  914.    function calls */
  915. int
  916. copy_dir_dir (char *s, char *d, int toplevel,
  917.           int move_over, int delete,
  918.               struct link *parent_dirs,
  919.           long *progress_count,
  920.           double *progress_bytes)
  921. {
  922. #ifdef __os2__
  923.     DIR    *next;
  924. #else
  925.     struct dirent *next;
  926. #endif
  927.     struct stat   buf, cbuf;
  928.     DIR    *reading;
  929.     char   *path, *mdpath, *dest_file, *dest_dir;
  930.     int    return_status = FILE_CONT;
  931.     struct utimbuf utb;
  932.     struct link *lp;
  933.  
  934.     /* First get the mode of the source dir */
  935.  retry_src_stat:
  936.     if ((*file_mask_xstat) (s, &cbuf)){
  937.     return_status = file_error (_(" Cannot stat source directory \"%s\" \n %s "), s);
  938.     if (return_status == FILE_RETRY)
  939.         goto retry_src_stat;
  940.     return return_status;
  941.     }
  942.     
  943.     if (is_in_linklist (dest_dirs, s, &cbuf)){
  944.     /* Don't copy a directory we created before (we don't want to copy 
  945.        infinitely if a directory is copied into itself) */
  946.     /* FIXME: should there be an error message and FILE_SKIP? - Norbert */
  947.     return FILE_CONT;
  948.     }
  949.  
  950. /* Hmm, hardlink to directory??? - Norbert */    
  951. /* FIXME: In this step we should do something
  952.    in case the destination already exist */    
  953.     /* Check the hardlinks */
  954.     if (file_mask_preserve && cbuf.st_nlink > 1 && check_hardlinks (s, d, &cbuf) == 1){
  955.         /* We have made a hardlink - no more processing is necessary */
  956.         return return_status;
  957.     }
  958.  
  959.     if (!S_ISDIR (cbuf.st_mode)){
  960.     return_status = file_error (_(" Source directory \"%s\" is not a directory \n %s "), s);
  961.     if (return_status == FILE_RETRY)
  962.         goto retry_src_stat;
  963.     return return_status;
  964.     }
  965.     
  966.     if (is_in_linklist (parent_dirs, s, &cbuf)){
  967.      /* we found a cyclic symbolic link */
  968.        message_2s (1, MSG_ERROR, _(" Cannot copy cyclic symbolic link \n `%s' "), s);
  969.     return FILE_SKIP;
  970.     }
  971.     
  972.     lp = xmalloc (sizeof (struct link), "parent_dirs");
  973.     lp->vfs = vfs_type (s);
  974.     lp->ino = cbuf.st_ino;
  975.     lp->dev = cbuf.st_dev;
  976.     lp->next = parent_dirs;
  977.     parent_dirs = lp;
  978.  
  979.     /* Now, check if the dest dir exists, if not, create it. */
  980.     if (mc_stat (d, &buf)){
  981.         /* Here the dir doesn't exist : make it !*/
  982.  
  983.         if (move_over){
  984.             if (mc_rename (s, d) == 0){
  985.         free (parent_dirs);
  986.         return FILE_CONT;
  987.         }
  988.     }
  989.     dest_dir = copy_strings (d, 0);
  990.     } else {
  991.         /*
  992.          * If the destination directory exists, we want to copy the whole
  993.          * directory, but we only want this to happen once.
  994.      *
  995.      * Escape sequences added to the * to compiler warnings.
  996.          * so, say /bla exists, if we copy /tmp/\* to /bla, we get /bla/tmp/\*
  997.          * or ( /bla doesn't exist )       /tmp/\* to /bla     ->  /bla/\*
  998.          */
  999. #if 1
  1000. /* Again, I'm getting curious. Is not d already what we wanted, incl.
  1001.  *  masked source basename? Is not this just a relict of the past versions? 
  1002.  *  I'm afraid this will lead into a two level deep dive :(
  1003.  *
  1004.  * I think this is indeed the problem.  I can not remember any case where
  1005.  * we actually would like that behaviour -miguel
  1006.  *
  1007.  * It's a documented feature (option `Dive into subdir if exists' in the
  1008.  * copy/move dialog). -Norbert
  1009.  */
  1010.         if (toplevel && dive_into_subdirs){
  1011.         dest_dir = concat_dir_and_file (d, x_basename (s));
  1012.     } else 
  1013. #endif
  1014.     {
  1015.         dest_dir = copy_strings (d, 0);
  1016.         goto dont_mkdir;
  1017.     }
  1018.     }
  1019.  retry_dst_mkdir:
  1020.     if (my_mkdir (dest_dir, (cbuf.st_mode & file_mask_umask_kill) | S_IRWXU)){
  1021.     return_status = file_error (_(" Cannot create target directory \"%s\" \n %s "), dest_dir);
  1022.     if (return_status == FILE_RETRY)
  1023.         goto retry_dst_mkdir;
  1024.     goto ret;
  1025.     }
  1026.     
  1027.     lp = xmalloc (sizeof (struct link), "dest_dirs");
  1028.     mc_stat (dest_dir, &buf);
  1029.     lp->vfs = vfs_type (dest_dir);
  1030.     lp->ino = buf.st_ino;
  1031.     lp->dev = buf.st_dev;
  1032.     lp->next = dest_dirs;
  1033.     dest_dirs = lp;
  1034.     
  1035. #ifndef OS2_NT 
  1036.     if (file_mask_preserve_uidgid){
  1037.      retry_dst_chown:
  1038.         if (mc_chown (dest_dir, cbuf.st_uid, cbuf.st_gid)){
  1039.         return_status = file_error
  1040.             (_(" Cannot chown target directory \"%s\" \n %s "), dest_dir);
  1041.         if (return_status == FILE_RETRY)
  1042.             goto retry_dst_chown;
  1043.         goto ret;
  1044.         }
  1045.     }
  1046. #endif
  1047.  
  1048.  dont_mkdir:
  1049.     /* open the source dir for reading */
  1050.     if ((reading = mc_opendir (s)) == 0){
  1051.     goto ret;
  1052.     }
  1053.     
  1054.     while ((next = mc_readdir (reading)) && return_status != FILE_ABORT){
  1055.         /*
  1056.          * Now, we don't want '.' and '..' to be created / copied at any time 
  1057.          */
  1058.         if (!strcmp (next->d_name, "."))
  1059.             continue;
  1060.         if (!strcmp (next->d_name, ".."))
  1061.            continue;
  1062.  
  1063.         /* get the filename and add it to the src directory */
  1064.     path = concat_dir_and_file (s, next->d_name);
  1065.     
  1066.         (*file_mask_xstat)(path, &buf);
  1067.         if (S_ISDIR (buf.st_mode)){
  1068.             mdpath = concat_dir_and_file (dest_dir, next->d_name);
  1069.             /*
  1070.              * From here, we just intend to recursively copy subdirs, not
  1071.              * the double functionality of copying different when the target
  1072.              * dir already exists. So, we give the recursive call the flag 0
  1073.              * meaning no toplevel.
  1074.              */
  1075.             return_status = copy_dir_dir (
  1076.             path, mdpath, 0, 0, delete, parent_dirs,
  1077.             progress_count, progress_bytes);
  1078.         free (mdpath);
  1079.     } else {
  1080.         dest_file = concat_dir_and_file (dest_dir, x_basename (path));
  1081.             return_status = copy_file_file (
  1082.             path, dest_file, 1,
  1083.             progress_count, progress_bytes, 0);
  1084.         free (dest_file);
  1085.     }    
  1086.     if (delete && return_status == FILE_CONT){
  1087.         if (erase_at_end){
  1088.                 static struct link *tail;
  1089.         lp = xmalloc (sizeof (struct link) + strlen (path), "erase_list");
  1090.         strcpy (lp->name, path);
  1091.         lp->st_mode = buf.st_mode;
  1092.         lp->next = 0;
  1093.                 if (erase_list){
  1094.                    tail->next = lp;
  1095.                    tail = lp;
  1096.                 } else 
  1097.            erase_list = tail = lp;
  1098.         } else {
  1099.             if (S_ISDIR (buf.st_mode)){
  1100.             return_status = erase_dir_iff_empty (path);
  1101.         } else
  1102.             return_status = erase_file (path, 0, 0, 0);
  1103.         }
  1104.     }
  1105.     
  1106. #ifdef __os2__
  1107.     /* The OS/2 mc_readdir returns a block of memory DIR
  1108.      * next should be freed: .ado
  1109.      */
  1110.     if (!next)
  1111.         free (next);
  1112. #endif
  1113.         free (path);
  1114.     }
  1115.     mc_closedir (reading);
  1116.     
  1117.     /* .ado: Directories can not have permission set in OS/2 */
  1118. #ifndef __os2__
  1119.     if (file_mask_preserve){
  1120.     mc_chmod (dest_dir, cbuf.st_mode & file_mask_umask_kill);
  1121.     utb.actime = cbuf.st_atime;
  1122.     utb.modtime = cbuf.st_mtime;
  1123.     mc_utime(dest_dir, &utb);
  1124.     }
  1125.  
  1126. #endif
  1127. ret:
  1128.     free (dest_dir);
  1129.     free (parent_dirs);
  1130.     return return_status;
  1131. }
  1132.  
  1133. /* }}} */
  1134.  
  1135. /* {{{ Move routines */
  1136.  
  1137. int
  1138. move_file_file (char *s, 
  1139.                 char *d, 
  1140.                 long *progress_count, 
  1141.                 double *progress_bytes)
  1142. {
  1143.     struct stat src_stats, dst_stats;
  1144.     int return_status = FILE_CONT;
  1145.  
  1146.     if (file_progress_show_source (s) == FILE_ABORT
  1147.     || file_progress_show_target (d) == FILE_ABORT)
  1148.     return FILE_ABORT;
  1149.  
  1150.     mc_refresh ();
  1151.  
  1152.  retry_src_lstat:
  1153.     if (mc_lstat (s, &src_stats) != 0){
  1154.     /* Source doesn't exist */
  1155.     return_status = file_error (_(" Cannot stat file \"%s\" \n %s "), s);
  1156.     if (return_status == FILE_RETRY)
  1157.         goto retry_src_lstat;
  1158.     return return_status;
  1159.     }
  1160.  
  1161.     if (mc_lstat (d, &dst_stats) == 0){
  1162.     /* Destination already exists */
  1163.     /* .ado: for OS/2 and NT, no st_ino exists */
  1164. #ifndef OS2_NT
  1165.     if (src_stats.st_dev == dst_stats.st_dev
  1166.         && src_stats.st_ino == dst_stats.st_ino){
  1167.         int msize = COLS - 36;
  1168.             char st[MC_MAXPATHLEN];
  1169.             char dt[MC_MAXPATHLEN];
  1170.  
  1171.         if (msize < 0)
  1172.         msize = 40;
  1173.         msize /= 2;
  1174.  
  1175.         strcpy (st, name_trunc (s, msize));
  1176.         strcpy (dt, name_trunc (d, msize));
  1177.         message_3s (1, MSG_ERROR, _(" `%s' and `%s' are the same file "),
  1178.              st, dt );
  1179.         do_refresh ();
  1180.         return FILE_SKIP;
  1181.     }
  1182. #endif /* OS2_NT */
  1183.     if (S_ISDIR (dst_stats.st_mode)){
  1184.         message_2s (1, MSG_ERROR, _(" Cannot overwrite directory `%s' "), d);
  1185.         do_refresh ();
  1186.         return FILE_SKIP;
  1187.     }
  1188.  
  1189.     if (confirm_overwrite){
  1190.         return_status = query_replace (d, &src_stats, &dst_stats);
  1191.         if (return_status != FILE_CONT)
  1192.         return return_status;
  1193.     }
  1194.     /* Ok to overwrite */
  1195.     }
  1196.  
  1197.     if (!file_progress_do_append){
  1198.     if (S_ISLNK (src_stats.st_mode) && file_mask_stable_symlinks){
  1199.         if ((return_status = make_symlink (s, d)) == FILE_CONT)
  1200.         goto retry_src_remove;
  1201.         else
  1202.         return return_status;
  1203.     }
  1204.  
  1205.         if (mc_rename (s, d) == 0)
  1206.         return FILE_CONT;
  1207.     }
  1208. #if 0
  1209. /* Comparison to EXDEV seems not to work in nfs if you're moving from
  1210.    one nfs to the same, but on the server it is on two different
  1211.    filesystems. Then nfs returns EIO instead of EXDEV. 
  1212.    Hope it will not hurt if we always in case of error try to copy/delete. */
  1213.      else
  1214.         errno = EXDEV; /* Hack to copy (append) the file and then delete it */
  1215.  
  1216.     if (errno != EXDEV){
  1217.     return_status = files_error (_(" Cannot move file \"%s\" to \"%s\" \n %s "), s, d);
  1218.     if (return_status == FILE_RETRY)
  1219.         goto retry_rename;
  1220.     return return_status;
  1221.     }
  1222. #endif    
  1223.  
  1224.     /* Failed because filesystem boundary -> copy the file instead */
  1225.     return_status = copy_file_file (s, d, 0, 
  1226.                              progress_count, progress_bytes, 1);
  1227.     if (return_status != FILE_CONT)
  1228.     return return_status;
  1229.  
  1230.     if ((return_status = file_progress_show_source (NULL)) != FILE_CONT
  1231.     || (return_status = file_progress_show (0, 0)) != FILE_CONT)
  1232.     return return_status;
  1233.  
  1234.     mc_refresh ();
  1235.  
  1236.  retry_src_remove:
  1237.     if (mc_unlink (s)){
  1238.     return_status = file_error (_(" Cannot remove file \"%s\" \n %s "), s);
  1239.     if (return_status == FILE_RETRY)
  1240.         goto retry_src_remove;
  1241.     return return_status;
  1242.     }
  1243.  
  1244.     if (return_status == FILE_CONT)
  1245.         return_status = progress_update_one (progress_count, 
  1246.                                   progress_bytes, src_stats.st_size, 1);
  1247.     
  1248.     return return_status;
  1249. }
  1250.  
  1251. int
  1252. move_dir_dir (char *s, char *d, long *progress_count, double *progress_bytes)
  1253. {
  1254.     struct stat sbuf, dbuf, destbuf;
  1255.     struct link *lp;
  1256.     char *destdir;
  1257.     int return_status;
  1258.     int move_over = 0;
  1259.  
  1260.     if (file_progress_show_source (s) == FILE_ABORT || 
  1261.     file_progress_show_target (d) == FILE_ABORT)
  1262.     return FILE_ABORT;
  1263.  
  1264.     mc_refresh ();
  1265.  
  1266.     mc_stat (s, &sbuf);
  1267.     if (mc_stat (d, &dbuf))
  1268.     destdir = copy_strings (d, 0);  /* destination doesn't exist */
  1269.     else if (!dive_into_subdirs){
  1270.     destdir = copy_strings (d, 0);
  1271.     move_over = 1;
  1272.     } else
  1273.     destdir = concat_dir_and_file (d, x_basename (s));
  1274.  
  1275.     /* Check if the user inputted an existing dir */
  1276.  retry_dst_stat:
  1277.     if (!mc_stat (destdir, &destbuf)){
  1278.     if (move_over){
  1279.         return_status = copy_dir_dir (
  1280.             s, destdir, 0, 1, 1, 0,
  1281.             progress_count, progress_bytes);
  1282.         
  1283.         if (return_status != FILE_CONT)
  1284.         goto ret;
  1285.         goto oktoret;
  1286.     } else {
  1287.         if (S_ISDIR (destbuf.st_mode))
  1288.             return_status = file_error (_(" Cannot overwrite directory \"%s\" %s "), destdir);
  1289.         else
  1290.             return_status = file_error (_(" Cannot overwrite file \"%s\" %s "), destdir);
  1291.         if (return_status == FILE_RETRY)
  1292.             goto retry_dst_stat;
  1293.     }
  1294.         free (destdir);
  1295.         return return_status;
  1296.     }
  1297.     
  1298.  retry_rename:
  1299.     if (mc_rename (s, destdir) == 0){
  1300.     return_status = FILE_CONT;
  1301.     goto ret;
  1302.     }
  1303. /* .ado: Drive, Do we need this anymore? */
  1304. #ifdef WIN32
  1305.         else {
  1306.         /* EXDEV: cross device; does not work everywhere */
  1307.                 if (toupper(s[0]) != toupper(destdir[0]))
  1308.             goto w32try;
  1309.         }
  1310. #endif
  1311.  
  1312.     if (errno != EXDEV){
  1313.     return_status = files_error (_(" Cannot move directory \"%s\" to \"%s\" \n %s "), s, d);
  1314.     if (return_status == FILE_RETRY)
  1315.         goto retry_rename;
  1316.     goto ret;
  1317.     }
  1318.  
  1319. w32try:
  1320.     /* Failed because of filesystem boundary -> copy dir instead */
  1321.     return_status = copy_dir_dir (s, destdir, 0, 0, 1, 0, progress_count, progress_bytes);
  1322.     
  1323.     if (return_status != FILE_CONT)
  1324.     goto ret;
  1325. oktoret:
  1326.     if ((return_status = file_progress_show_source (NULL)) != FILE_CONT
  1327.     || (return_status = file_progress_show (0, 0)) != FILE_CONT)
  1328.     goto ret;
  1329.  
  1330.     mc_refresh ();
  1331.     if (erase_at_end){
  1332.     for ( ; erase_list && return_status != FILE_ABORT; ){
  1333.             if (S_ISDIR (erase_list->st_mode)){
  1334.         return_status = erase_dir_iff_empty (erase_list->name);
  1335.         } else
  1336.         return_status = erase_file (erase_list->name, 0, 0, 0);
  1337.         lp = erase_list;
  1338.         erase_list = erase_list->next;
  1339.         free (lp);
  1340.     }
  1341.     }
  1342.     erase_dir_iff_empty (s);
  1343.  
  1344.  ret:
  1345.     free (destdir);
  1346.     for ( ; erase_list; ){
  1347.        lp = erase_list;
  1348.        erase_list = erase_list->next;
  1349.        free (lp);
  1350.     }
  1351.     return return_status;
  1352. }
  1353.  
  1354. /* }}} */
  1355.  
  1356. /* {{{ Erase routines */
  1357. /* Don't update progress status if progress_count==NULL */
  1358. int
  1359. erase_file (char *s, 
  1360.             long *progress_count, 
  1361.             double *progress_bytes, 
  1362.             int is_toplevel_file)
  1363. {
  1364.     int return_status;
  1365.     struct stat buf;
  1366.  
  1367.     if (file_progress_show_deleting (s) == FILE_ABORT)
  1368.     return FILE_ABORT;
  1369.     mc_refresh ();
  1370.     
  1371.     if (progress_count && mc_stat (s, &buf)) {
  1372.         /* ignore, most likely the mc_unlink fails, too */
  1373.         buf.st_size = 0;
  1374.     }
  1375.  
  1376.     while (mc_unlink (s)){
  1377.     return_status = file_error (_(" Cannot delete file \"%s\" \n %s "), s);
  1378.     if (return_status != FILE_RETRY)
  1379.         return return_status;
  1380.     }
  1381.     if (progress_count)
  1382.         return progress_update_one (
  1383.                      progress_count, progress_bytes, 
  1384.                      buf.st_size, is_toplevel_file);
  1385.     else
  1386.         return FILE_CONT;
  1387. }
  1388.  
  1389. static int
  1390. recursive_erase (char *s, long *progress_count, double *progress_bytes)
  1391. {
  1392.     struct dirent *next;
  1393.     struct stat    buf;
  1394.     DIR    *reading;
  1395.     char   *path;
  1396.     int    return_status = FILE_CONT;
  1397.  
  1398.     if (!strcmp (s, ".."))
  1399.     return 1;
  1400.     
  1401.     reading = mc_opendir (s);
  1402.     
  1403.     if (!reading)
  1404.     return 1;
  1405.     
  1406.     while ((next = mc_readdir (reading)) && return_status == FILE_CONT){
  1407.     if (!strcmp (next->d_name, "."))
  1408.         continue;
  1409.        if (!strcmp (next->d_name, ".."))
  1410.         continue;
  1411.     path = concat_dir_and_file (s, next->d_name);
  1412.        if (mc_lstat (path, &buf)){
  1413.         free (path);
  1414.         return 1;
  1415.     } 
  1416.     if (S_ISDIR (buf.st_mode))
  1417.         return_status = (recursive_erase (path, progress_count, progress_bytes) != FILE_CONT);
  1418.     else
  1419.         return_status = erase_file (
  1420.                                   path, progress_count, progress_bytes, 0);
  1421.     free (path);
  1422.     /* .ado: OS/2 returns a block of memory DIR to next and must be freed */
  1423. #ifdef __os2__
  1424.     if (!next)
  1425.         free (next);
  1426. #endif
  1427.     }
  1428.     mc_closedir (reading);
  1429.     if (return_status != FILE_CONT)
  1430.     return return_status;
  1431.     if (file_progress_show_deleting (s) == FILE_ABORT)
  1432.     return FILE_ABORT;
  1433.     mc_refresh ();
  1434.  retry_rmdir:
  1435.     if (my_rmdir (s)){
  1436.     return_status = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
  1437.     if (return_status == FILE_RETRY)
  1438.         goto retry_rmdir;
  1439.     return return_status;
  1440.     }
  1441.     return FILE_CONT;
  1442. }
  1443.  
  1444. /* Return -1 on error, 1 if there are no entries besides "." and ".." 
  1445.    in the directory path points to, 0 else. */
  1446. static int 
  1447. check_dir_is_empty(char *path)
  1448. {
  1449.     DIR *dir;
  1450.     struct dirent *d;
  1451.     int i;
  1452.  
  1453.     dir = mc_opendir (path);
  1454.     if (!dir)
  1455.     return -1;
  1456.  
  1457.     for (i = 1, d = mc_readdir (dir); d; d = mc_readdir (dir)){
  1458.     if (d->d_name[0] == '.' && (d->d_name[1] == '\0' ||
  1459.             (d->d_name[1] == '.' && d->d_name[2] == '\0')))
  1460.         continue; /* "." or ".." */
  1461.     i = 0;
  1462.     break;
  1463.     }
  1464.  
  1465.     mc_closedir (dir);
  1466.     return i;
  1467. }
  1468.  
  1469. int
  1470. erase_dir (char *s, long *progress_count, double *progress_bytes)
  1471. {
  1472.     int error;
  1473.  
  1474.     if (strcmp (s, "..") == 0)
  1475.     return FILE_SKIP;
  1476.  
  1477.     if (strcmp (s, ".") == 0)
  1478.     return FILE_SKIP;
  1479.  
  1480.     if (file_progress_show_deleting (s) == FILE_ABORT)
  1481.     return FILE_ABORT;
  1482.     mc_refresh ();
  1483.  
  1484.     /* The old way to detect a non empty directory was:
  1485.             error = my_rmdir (s);
  1486.             if (error && (errno == ENOTEMPTY || errno == EEXIST))){
  1487.        For the linux user space nfs server (nfs-server-2.2beta29-2)
  1488.        we would have to check also for EIO. I hope the new way is
  1489.        fool proof. (Norbert)
  1490.      */
  1491.     error = check_dir_is_empty (s);
  1492.     if (error == 0){ /* not empty */
  1493.     error = query_recursive (s);
  1494.     if (error == FILE_CONT)
  1495.         return recursive_erase (s, progress_count, progress_bytes);
  1496.     else
  1497.         return error;
  1498.     }
  1499.  
  1500.  retry_rmdir:
  1501.     error = my_rmdir (s);
  1502.     if (error == -1){
  1503.     error = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
  1504.     if (error == FILE_RETRY)
  1505.         goto retry_rmdir;
  1506.     return error;
  1507.     }
  1508.     return FILE_CONT;
  1509. }
  1510.  
  1511. int
  1512. erase_dir_iff_empty (char *s)
  1513. {
  1514.     int error;
  1515.  
  1516.     if (strcmp (s, "..") == 0)
  1517.     return FILE_SKIP;
  1518.  
  1519.     if (strcmp (s, ".") == 0)
  1520.     return FILE_SKIP;
  1521.  
  1522.     if (file_progress_show_deleting (s) == FILE_ABORT)
  1523.     return FILE_ABORT;
  1524.     mc_refresh ();
  1525.  
  1526.     if (1 != check_dir_is_empty (s)) /* not empty or error */
  1527.     return FILE_CONT;
  1528.  
  1529.  retry_rmdir:
  1530.     error = my_rmdir (s);
  1531.     if (error){
  1532.     error = file_error (_(" Cannot remove directory \"%s\" \n %s "), s);
  1533.     if (error == FILE_RETRY)
  1534.         goto retry_rmdir;
  1535.     return error;
  1536.     }
  1537.     return FILE_CONT;
  1538. }
  1539.  
  1540. /* }}} */
  1541.  
  1542. /* {{{ Panel operate routines */
  1543.  
  1544. /* Returns currently selected file or the first marked file if there is one */
  1545. char *
  1546. panel_get_file (WPanel *panel, struct stat *stat_buf)
  1547. {
  1548.     int i;
  1549.  
  1550.     /* No problem with Gnome, as get_current_type never returns view_tree there */
  1551.     if (get_current_type () == view_tree){
  1552.       WTree *tree = (WTree *)get_panel_widget (get_current_index ());
  1553.     
  1554.     mc_stat (tree->selected_ptr->name, stat_buf);
  1555.     return tree->selected_ptr->name;
  1556.     } 
  1557.  
  1558.     if (panel->marked){
  1559.     for (i = 0; i < panel->count; i++)
  1560.         if (panel->dir.list [i].f.marked){
  1561.         *stat_buf = panel->dir.list [i].buf;
  1562.         return panel->dir.list [i].fname;
  1563.         }
  1564.     } else {
  1565.     *stat_buf = panel->dir.list [panel->selected].buf;
  1566.     return panel->dir.list [panel->selected].fname;
  1567.     }
  1568.     fprintf (stderr, _(" Internal error: get_file \n"));
  1569.     mi_getch ();
  1570.     return "";
  1571. }
  1572.  
  1573. int
  1574. is_wildcarded (char *p)
  1575. {
  1576.     for (; *p; p++){
  1577.         if (*p == '*')
  1578.             return 1;
  1579.         else if (*p == '\\' && p [1] >= '1' && p [1] <= '9')
  1580.             return 1;
  1581.     }
  1582.     return 0;
  1583. }
  1584.  
  1585. /* Sets all global variables used by copy_file_file/move_file_file to a 
  1586.    resonable default
  1587.    (file_mask_dialog sets these global variables interactively)
  1588.  */
  1589. void
  1590. file_mask_defaults (void)
  1591. {
  1592.     file_mask_stable_symlinks = 0;
  1593.     file_mask_op_follow_links = 0;
  1594.     dive_into_subdirs = 0;
  1595.     file_mask_xstat = mc_lstat;
  1596.     
  1597.     file_mask_preserve = 1;
  1598.     file_mask_umask_kill = 0777777;
  1599.     file_mask_preserve_uidgid = (geteuid () == 0) ? 1 : 0;
  1600. }
  1601.  
  1602. /**
  1603.  * compute_dir_size:
  1604.  *
  1605.  * Computes the number of bytes used by the files in a directory
  1606.  */
  1607. void
  1608. compute_dir_size (char *dirname, long *ret_marked, double *ret_total)
  1609. {
  1610.     DIR *dir;
  1611.     struct dirent *dirent;
  1612.     
  1613.     dir = mc_opendir (dirname);
  1614.  
  1615.     if (!dir)
  1616.         return;
  1617.  
  1618.     while ((dirent = mc_readdir (dir)) != NULL){
  1619.         struct stat s;
  1620.         char *fullname;
  1621.         int res;
  1622.         
  1623.         if (strcmp (dirent->d_name, ".") == 0)
  1624.             continue;
  1625.         if (strcmp (dirent->d_name, "..") == 0)
  1626.             continue;
  1627.  
  1628.         fullname = concat_dir_and_file (dirname, dirent->d_name); 
  1629.         
  1630.         res = mc_lstat (fullname, &s);
  1631.  
  1632.         if (res != 0){
  1633.             free (fullname);
  1634.             continue;
  1635.         }
  1636.  
  1637.         if (S_ISDIR (s.st_mode)){
  1638.             long   subdir_count = 0;
  1639.             double subdir_bytes = 0;
  1640.  
  1641.             compute_dir_size (fullname, &subdir_count, &subdir_bytes);
  1642.  
  1643.             *ret_marked += subdir_count;
  1644.             *ret_total  += subdir_bytes;
  1645.         } else {
  1646.             (*ret_marked)++;
  1647.             *ret_total += s.st_size;
  1648.         }
  1649.         free (fullname);
  1650.     }
  1651.     
  1652.     mc_closedir (dir);
  1653. }
  1654.  
  1655. /**
  1656.  * panel_compute_totals:
  1657.  *
  1658.  * compute the number of files and the number of bytes
  1659.  * used up by the whole selection, recursing directories
  1660.  * as required.  In addition, it checks to see if it will
  1661.  * overwrite any files by doing the copy.
  1662.  */
  1663. static void
  1664. panel_compute_totals (WPanel *panel, long *ret_marked, double *ret_total)
  1665. {
  1666.     int i;
  1667.     
  1668.         *ret_marked = 0;
  1669.         *ret_total = 0.0;
  1670.  
  1671.     for (i = 0; i < panel->count; i++){
  1672.         struct stat *s;
  1673.         
  1674.         if (!panel->dir.list [i].f.marked)
  1675.             continue;
  1676.  
  1677.         s = &panel->dir.list [i].buf;
  1678.  
  1679.         if (S_ISDIR (s->st_mode)){
  1680.             char   *dir_name;
  1681.             long   subdir_count = 0;
  1682.             double subdir_bytes = 0;
  1683.  
  1684.             dir_name = concat_dir_and_file (panel->cwd, panel->dir.list [i].fname);
  1685.             compute_dir_size (dir_name, &subdir_count, &subdir_bytes);
  1686.  
  1687.             *ret_marked += subdir_count;
  1688.             *ret_total  += subdir_bytes;
  1689.             free (dir_name);
  1690.         } else {
  1691.             (*ret_marked)++;
  1692.             *ret_total += s->st_size;
  1693.         }
  1694.     }
  1695. }
  1696.  
  1697. /**
  1698.  * panel_operate:
  1699.  *
  1700.  * Performs one of the operations on the selection on the source_panel
  1701.  * (copy, delete, move).  
  1702.  *
  1703.  * Returns 1 if did change the directory
  1704.  * structure, Returns 0 if user aborted
  1705.  */
  1706. static int
  1707. panel_operate_flags (void *source_panel, FileOperation operation, char *thedefault, int ask_user)
  1708. {
  1709.     WPanel *panel = source_panel;
  1710. #ifdef WITH_FULL_PATHS
  1711.     char *source_with_path = NULL;
  1712. #else
  1713. #   define source_with_path source
  1714. #endif
  1715.     char *source = NULL;
  1716.     char *dest = NULL;
  1717.     char *temp = NULL;
  1718.     char *save_cwd = NULL, *save_dest = NULL;
  1719.     int only_one = (get_current_type () == view_tree) || (panel->marked <= 1);
  1720.     struct stat src_stat, dst_stat;
  1721.     int i, value;
  1722.  
  1723.     long   count = 0;
  1724.     double bytes = 0;
  1725.  
  1726.     int  dst_result;
  1727.     int  do_bg;            /* do background operation? */
  1728.  
  1729.     do_bg = 0;
  1730.     file_mask_rx.buffer = NULL;
  1731.     free_linklist (&linklist);
  1732.     free_linklist (&dest_dirs);
  1733.     if (get_current_type () == view_listing)
  1734.     if (!panel->marked && !strcmp (selection (panel)->fname, "..")){
  1735.         message (1, MSG_ERROR, _(" Can't operate on \"..\"! "));
  1736.         return 0;
  1737.     }
  1738.     
  1739.     if (operation < OP_COPY || operation > OP_DELETE)
  1740.     return 0;
  1741.     
  1742.     /* Generate confirmation prompt */
  1743.     source = panel_operate_generate_prompt (cmd_buf, panel, operation, only_one, &src_stat);   
  1744.     
  1745.     /* Show confirmation dialog */
  1746.     if (operation == OP_DELETE && confirm_delete){
  1747.         if (know_not_what_am_i_doing)
  1748.         query_set_sel (1);
  1749. #ifdef HAVE_GNOME
  1750.     i = query_dialog (_(op_names [operation]), cmd_buf,
  1751.               D_ERROR, 2, _("Yes"), _("No"));
  1752. #else
  1753.     i = query_dialog (_(op_names [operation]), cmd_buf,
  1754.               D_ERROR, 2, _("&Yes"), _("&No"));
  1755. #endif
  1756.     if (i != 0)
  1757.         return 0;
  1758.     } else if (operation != OP_DELETE){
  1759.     file_mask_rx.buffer = (char *) xmalloc (MC_MAXPATHLEN, "mask copying");
  1760.     file_mask_rx.allocated = MC_MAXPATHLEN;
  1761.     file_mask_rx.translate = 0;
  1762.     
  1763.         if (ask_user){
  1764.         char *dest_dir;
  1765.         
  1766.         if (thedefault != NULL)
  1767.         dest_dir = thedefault;
  1768.         else if (get_other_type () == view_listing)
  1769.         dest_dir = opanel->cwd;
  1770.         else
  1771.         dest_dir = panel->cwd;
  1772.         
  1773.         dest = file_mask_dialog (operation, cmd_buf, dest_dir, only_one, &do_bg);
  1774.         if (!dest){
  1775.         free (file_mask_rx.buffer);
  1776.         return 0;
  1777.         }
  1778.         if (!*dest){
  1779.         free (file_mask_rx.buffer);
  1780.         free (dest);
  1781.         return 0;
  1782.         }
  1783.     } else {
  1784.         char *all = "^\\(.*\\)$";
  1785.         
  1786.         re_compile_pattern (all, strlen (all), &file_mask_rx);
  1787.         file_mask_dest_mask = strdup ("*");
  1788.         do_bg = FALSE;
  1789.         dest = strdup (thedefault);
  1790.         file_mask_defaults ();
  1791.     }
  1792.     }
  1793.  
  1794. #ifdef WITH_BACKGROUND
  1795.     /* Did the user select to do a background operation? */
  1796.     if (do_bg){
  1797.     int v;
  1798.     
  1799.     v = do_background (copy_strings (operation_names [operation], ": ", panel->cwd, 0));
  1800.     if (v == -1){
  1801.         message (1, MSG_ERROR, _(" Sorry, I could not put the job in background "));
  1802.     }
  1803.  
  1804.     /* If we are the parent */
  1805.     if (v == 1){
  1806.         mc_setctl (panel->cwd, MCCTL_FORGET_ABOUT, NULL);
  1807.         mc_setctl (dest,       MCCTL_FORGET_ABOUT, NULL);
  1808.         return 0;
  1809.     }
  1810.     }
  1811. #endif
  1812.  
  1813.     /* Initialize things */
  1814.     /* We do not want to trash cache every time file is
  1815.        created/touched. However, this will make our cache contain
  1816.        invalid data. */
  1817.     if (dest) {
  1818.     if (mc_setctl (dest, MCCTL_WANT_STALE_DATA, NULL))
  1819.         save_dest = strdup(dest);
  1820.     }
  1821.     if (panel->cwd) {
  1822.     if (mc_setctl (panel->cwd, MCCTL_WANT_STALE_DATA, NULL))
  1823.         save_cwd = strdup(panel->cwd);
  1824.     }
  1825.     
  1826.     ftpfs_hint_reread (0);
  1827.     
  1828.     /* Now, let's do the job */
  1829.  
  1830.     /* This code is only called by the tree and panel code */
  1831.     if (only_one){
  1832.       /* We now have ETA in all cases */
  1833.       create_op_win (operation, 1);
  1834.  
  1835.     /* One file: FIXME mc_chdir will take user out of any vfs */
  1836.     if (operation != OP_COPY && get_current_type () == view_tree)
  1837.         mc_chdir (PATH_SEP_STR);
  1838.     
  1839.     /* The source and src_stat variables have been initialized before */
  1840. #ifdef WITH_FULL_PATHS
  1841.     source_with_path = concat_dir_and_file (panel->cwd, source);
  1842. #endif
  1843.     
  1844.     if (operation == OP_DELETE)
  1845.     {
  1846.         if (S_ISDIR (src_stat.st_mode))
  1847.         value = erase_dir (source_with_path, &count, &bytes);
  1848.         else
  1849.         value = erase_file (source_with_path, &count, &bytes, 1);
  1850.     }
  1851.     else
  1852.     {
  1853.         temp = transform_source (source_with_path);
  1854.  
  1855.         if (temp == NULL){
  1856.         value = transform_error;
  1857.         }
  1858.         else
  1859.         {
  1860.         temp = get_full_name (dest, temp);
  1861.         free (dest);
  1862.         dest = temp;
  1863.         temp = 0;
  1864.  
  1865.             switch (operation){
  1866.             case OP_COPY:
  1867.             /*
  1868.              * we use file_mask_op_follow_links only with OP_COPY,
  1869.               */
  1870.             (*file_mask_xstat) (source_with_path, &src_stat);
  1871.  
  1872.             if (S_ISDIR (src_stat.st_mode))
  1873.                 value = copy_dir_dir (
  1874.                 source_with_path, dest, 1, 0, 0, 0, &count, &bytes);
  1875.             else
  1876.                 value = copy_file_file (
  1877.                 source_with_path, dest, 1, &count, &bytes, 1);
  1878.             break;
  1879.  
  1880.             case OP_MOVE:
  1881.             if (S_ISDIR (src_stat.st_mode))
  1882.                 value = move_dir_dir (
  1883.                 source_with_path, dest, &count, &bytes);
  1884.             else
  1885.                 value = move_file_file (
  1886.                 source_with_path, dest, &count, &bytes);
  1887.             break;
  1888.  
  1889.             default:
  1890.             value = FILE_CONT;
  1891.             message_1s (1, _(" Internal failure "), _(" Unknown file operation "));
  1892.             }
  1893.         }
  1894.     } /* Copy or move operation */
  1895.  
  1896.     if (value == FILE_CONT)
  1897.         unmark_files (panel);
  1898.  
  1899.     } else {
  1900.         /* Many files */
  1901.     /* Need to determine this.*/
  1902.     int policy_over_write_necessary = 1;
  1903.  
  1904.     /* Check destination for copy or move operation */
  1905.     if (operation != OP_DELETE){
  1906.     retry_many_dst_stat:
  1907.         dst_result = mc_stat (dest, &dst_stat);
  1908.         if (dst_result == 0 && !S_ISDIR (dst_stat.st_mode)){
  1909.         if (file_error (_(" Destination \"%s\" must be a directory \n %s "), dest) == FILE_RETRY)
  1910.             goto retry_many_dst_stat;
  1911.         goto clean_up;
  1912.         }
  1913.     }
  1914.  
  1915.     /* Initialize variables for progress bars */
  1916.     if (operation != OP_MOVE && verbose && file_op_compute_totals) {
  1917.         panel_compute_totals (
  1918.             panel, &file_progress_count, &file_progress_bytes);
  1919.                 file_progress_totals_computed = 1;
  1920.     } else {
  1921.                 file_progress_totals_computed = 0;
  1922.         file_progress_count = panel->marked;
  1923.         file_progress_bytes = panel->total;
  1924.     }
  1925.     
  1926.  
  1927. #ifdef HAVE_GNOME
  1928.     /* FIXME: we need to determine if this dialog is actually needed. */
  1929.     /* We need to pre-copy all the files and see if there are any 
  1930.      * over-writes.  Ugh, this sounds yucky.  )-: */
  1931.     if (policy_over_write_necessary) {
  1932.         if (file_progress_query_replace_policy (TRUE) == FILE_ABORT)
  1933.             goto clean_up;
  1934.         else
  1935.             /* this will initialize some variables */
  1936.             file_progress_query_replace_policy (FALSE);
  1937.     }
  1938. #endif
  1939.     /* We now have ETA in all cases */
  1940.     create_op_win (operation, 1);
  1941.       
  1942.     /* Loop for every file, perform the actual copy operation */
  1943.     for (i = 0; i < panel->count; i++){
  1944.  
  1945.         if (!panel->dir.list [i].f.marked)
  1946.         continue;    /* Skip the unmarked ones */
  1947.  
  1948.         source = panel->dir.list [i].fname;
  1949.         src_stat = panel->dir.list [i].buf;    /* Inefficient, should we use pointers? */
  1950.  
  1951. #ifdef WITH_FULL_PATHS
  1952.         if (source_with_path)
  1953.             free (source_with_path);
  1954.         source_with_path = concat_dir_and_file (panel->cwd, source);
  1955. #endif
  1956.  
  1957.         if (operation == OP_DELETE){
  1958.         if (S_ISDIR (src_stat.st_mode))
  1959.             value = erase_dir (source_with_path, &count, &bytes);
  1960.         else
  1961.             value = erase_file (source_with_path, &count, &bytes, 1);
  1962.         }
  1963.         else
  1964.         {
  1965.         if (temp)
  1966.             free (temp);
  1967.  
  1968.         temp = transform_source (source_with_path);
  1969.         if (temp == NULL)
  1970.             value = transform_error;
  1971.         else
  1972.         {
  1973.             temp = get_full_name (dest, temp);
  1974.  
  1975.             switch (operation){
  1976.             case OP_COPY:
  1977.                /*
  1978.             * we use file_mask_op_follow_links only with OP_COPY,
  1979.                 */
  1980.             (*file_mask_xstat) (source_with_path, &src_stat);
  1981.                 if (S_ISDIR (src_stat.st_mode))
  1982.                 value = copy_dir_dir (
  1983.                     source_with_path, temp, 1, 0, 0, 0,
  1984.                     &count, &bytes);
  1985.                 else
  1986.                 value = copy_file_file (
  1987.                     source_with_path, temp, 1,
  1988.                     &count, &bytes, 1);
  1989.             free_linklist (&dest_dirs);
  1990.                 break;
  1991.  
  1992.             case OP_MOVE:
  1993.                 if (S_ISDIR (src_stat.st_mode))
  1994.                 value = move_dir_dir (
  1995.                     source_with_path, temp,
  1996.                     &count, &bytes);
  1997.                 else
  1998.                     value = move_file_file (
  1999.                     source_with_path, temp,
  2000.                     &count, &bytes);
  2001.                 break;
  2002.  
  2003.             default:
  2004.                 message_1s (1, _(" Internal failure "),
  2005.                      _(" Unknown file operation "));
  2006.                 goto clean_up;
  2007.             }
  2008.         }
  2009.  
  2010.         } /* Copy or move operation */
  2011.  
  2012.         if (value == FILE_ABORT)
  2013.         goto clean_up;
  2014.  
  2015.         if (value == FILE_CONT)
  2016.         do_file_mark (panel, i, 0);
  2017.  
  2018.         if (file_progress_show_count (count, file_progress_count) == FILE_ABORT)
  2019.         goto clean_up;
  2020.  
  2021.         if (verbose &&
  2022.             file_progress_show_bytes (bytes, file_progress_bytes) == FILE_ABORT)
  2023.         goto clean_up;
  2024.  
  2025.         if (operation != OP_DELETE && verbose
  2026.         && file_progress_show (0, 0) == FILE_ABORT)
  2027.         goto clean_up;
  2028.  
  2029.         mc_refresh ();
  2030.     } /* Loop for every file */
  2031.     } /* Many files */
  2032.  
  2033.  clean_up:
  2034.     /* Clean up */
  2035.     destroy_op_win ();
  2036.     if (save_cwd) {
  2037.         mc_setctl (save_cwd, MCCTL_NO_STALE_DATA, NULL);
  2038.     free(save_cwd);
  2039.     }
  2040.     if (save_dest) {
  2041.         mc_setctl (save_dest, MCCTL_NO_STALE_DATA, NULL);
  2042.     free(save_dest);
  2043.     }
  2044.     ftpfs_hint_reread (1);
  2045.  
  2046.     free_linklist (&linklist);
  2047.     free_linklist (&dest_dirs);
  2048. #if WITH_FULL_PATHS
  2049.     if (source_with_path)
  2050.         free (source_with_path);
  2051. #endif
  2052.  
  2053.     if (dest)
  2054.     free (dest);
  2055.  
  2056.     if (temp)
  2057.     free (temp);
  2058.  
  2059.     if (file_mask_rx.buffer){
  2060.     free (file_mask_rx.buffer);
  2061.     file_mask_rx.buffer = NULL;
  2062.     }
  2063.  
  2064.     if (file_mask_dest_mask){
  2065.     free (file_mask_dest_mask);
  2066.     file_mask_dest_mask = NULL;
  2067.     }
  2068.  
  2069. #ifdef WITH_BACKGROUND
  2070.     /* Let our parent know we are saying bye bye */
  2071.     if (we_are_background){
  2072.     vfs_shut ();
  2073.     tell_parent (MSG_CHILD_EXITING);
  2074.     exit (1);
  2075.     } 
  2076. #endif
  2077.     return 1;
  2078. }
  2079.  
  2080. int
  2081. panel_operate_def (void *source_panel, FileOperation operation, char *thedefault)
  2082. {
  2083.     return panel_operate_flags (source_panel, operation, thedefault, FALSE);
  2084. }
  2085.  
  2086. int
  2087. panel_operate (void *source_panel, FileOperation operation, char *thedefault)
  2088. {
  2089.     return panel_operate_flags (source_panel, operation, thedefault, TRUE);
  2090. }
  2091.  
  2092. /* }}} */
  2093.  
  2094. /* {{{ Query/status report routines */
  2095.  
  2096. static int
  2097. real_do_file_error (enum OperationMode mode, char *error)
  2098. {
  2099.     int result;
  2100.     char *msg;
  2101.  
  2102.     msg = mode == Foreground ? MSG_ERROR : _(" Background process error ");
  2103.     result = query_dialog (msg, error, D_ERROR, 3, _("&Skip"), _("&Retry"), _("&Abort"));
  2104.  
  2105.     switch (result){
  2106.     case 0:
  2107.     do_refresh ();
  2108.     return FILE_SKIP;
  2109.  
  2110.     case 1:
  2111.     do_refresh ();
  2112.     return FILE_RETRY;
  2113.  
  2114.     case 2:
  2115.     default:
  2116.     return FILE_ABORT;
  2117.     }
  2118. }
  2119.  
  2120. /* Report error with one file */
  2121. int
  2122. file_error (char *format, char *file)
  2123. {
  2124.     sprintf (cmd_buf, format,
  2125.          name_trunc (file, 30), unix_error_string (errno));
  2126.  
  2127.     return do_file_error (cmd_buf);
  2128. }
  2129.  
  2130. /* Report error with two files */
  2131. int
  2132. files_error (char *format, char *file1, char *file2)
  2133. {
  2134.     char nfile1 [16];
  2135.     char nfile2 [16];
  2136.     
  2137.     strcpy (nfile1, name_trunc (file1, 15));
  2138.     strcpy (nfile2, name_trunc (file2, 15));
  2139.     
  2140.     sprintf (cmd_buf, format, nfile1, nfile2, unix_error_string (errno));
  2141.  
  2142.     return do_file_error (cmd_buf);
  2143. }
  2144.  
  2145. static int
  2146. real_query_recursive (enum OperationMode mode, char *s)
  2147. {
  2148.     char *confirm, *text;
  2149.  
  2150.     if (file_progress_recursive_result < RECURSIVE_ALWAYS){
  2151.     char *msg =
  2152.         mode == Foreground ? _("\n   Directory not empty.   \n   Delete it recursively? ")
  2153.                            : _("\n   Background process: Directory not empty \n   Delete it recursively? ");
  2154.     text = copy_strings (" Delete: ", name_trunc (s, 30), " ", 0);
  2155.  
  2156.         if (know_not_what_am_i_doing)
  2157.         query_set_sel (1);
  2158.         file_progress_recursive_result = query_dialog (text, msg, D_ERROR, 5,
  2159.                                _("&Yes"), _("&No"),
  2160.                                _("a&ll"), _("non&E"),
  2161.                                _("&Abort"));
  2162.     
  2163.     if (file_progress_recursive_result != RECURSIVE_ABORT)
  2164.         do_refresh ();
  2165.     free (text);
  2166.     if (know_not_what_am_i_doing){
  2167.         if (file_progress_recursive_result == RECURSIVE_YES ||
  2168.         file_progress_recursive_result == RECURSIVE_ALWAYS){
  2169.         text = copy_strings (
  2170.             _(" Type 'yes' if you REALLY want to delete "),
  2171.             file_progress_recursive_result == RECURSIVE_YES
  2172.             ? name_trunc (s, 19) : _("all the directories "), " ", 0);
  2173.         confirm = input_dialog (
  2174.             mode == Foreground ? _(" Recursive Delete ")
  2175.             : _(" Background process: Recursive Delete "),
  2176.             text, "no");
  2177.         do_refresh ();
  2178.         if (!confirm || strcmp (confirm, "yes"))
  2179.             file_progress_recursive_result = RECURSIVE_NEVER;
  2180.         free (confirm);
  2181.         free (text);
  2182.         }
  2183.     }
  2184.     }
  2185.  
  2186.     switch (file_progress_recursive_result){
  2187.     case RECURSIVE_YES:
  2188.     case RECURSIVE_ALWAYS:
  2189.     return FILE_CONT;
  2190.  
  2191.     case RECURSIVE_NO:
  2192.     case RECURSIVE_NEVER:
  2193.     return FILE_SKIP;
  2194.  
  2195.     case RECURSIVE_ABORT:
  2196.  
  2197.     default:
  2198.     return FILE_ABORT;
  2199.     }
  2200. }
  2201.  
  2202. #ifdef WITH_BACKGROUND
  2203. int
  2204. do_file_error (char *str)
  2205. {
  2206.     return call_1s (real_do_file_error, str);
  2207. }
  2208.  
  2209. int
  2210. query_recursive (char *s)
  2211. {
  2212.     return call_1s (real_query_recursive, s);
  2213. }
  2214.  
  2215. int
  2216. query_replace (char *destname, struct stat *_s_stat, struct stat *_d_stat)
  2217. {
  2218.     if (we_are_background)
  2219.     return parent_call ((void *)file_progress_real_query_replace,
  2220.                 3,
  2221.                 strlen (destname), destname,
  2222.                 sizeof (struct stat), _s_stat,
  2223.                 sizeof(struct stat), _d_stat);
  2224.     else
  2225.     return file_progress_real_query_replace (Foreground, destname, _s_stat, _d_stat);
  2226. }
  2227.  
  2228. #else
  2229. do_file_error (char *str)
  2230. {
  2231.     return real_do_file_error (Foreground, str);
  2232. }
  2233.  
  2234. int
  2235. query_recursive (char *s)
  2236. {
  2237.     return real_query_recursive (Foreground, s);
  2238. }
  2239.  
  2240. int
  2241. query_replace (char *destname, struct stat *_s_stat, struct stat *_d_stat)
  2242. {
  2243.     return file_progress_real_query_replace (Foreground, destname, _s_stat, _d_stat);
  2244. }
  2245.  
  2246. #endif
  2247.  
  2248. /*
  2249.   Cause emacs to enter folding mode for this file:
  2250.   Local variables:
  2251.   end:
  2252. */
  2253.  
  2254. /*
  2255.  * This array introduced to avoid translation problems. The former (op_names)
  2256.  * is assumed to be nouns, suitable in dialog box titles; this one should
  2257.  * contain whatever is used in prompt itself (i.e. in russian, it's verb).
  2258.  * Notice first symbol - it is to fool gettext and force these strings to
  2259.  * be different for it. First symbol is skipped while building a prompt.
  2260.  * (I don't use spaces around the words, because someday they could be
  2261.  * dropped, when widgets get smarter)
  2262.  */
  2263. static char *op_names1 [] = { N_("1Copy"), N_("1Move"), N_("1Delete") };
  2264. #define    FMD_XLEN 64
  2265.  
  2266. int fmd_xlen = FMD_XLEN, fmd_i18n_flag = 0;
  2267. /*
  2268.  * These are formats for building a prompt. Parts encoded as follows:
  2269.  * %o - operation from op_names1
  2270.  * %f - file/files or files/directories, as appropriate
  2271.  * %m - "with source mask" or question mark for delete
  2272.  * %s - source name (truncated)
  2273.  * %d - number of marked files
  2274.  * %e - "to:" or question mark for delete
  2275.  */
  2276.  
  2277. #ifndef HAVE_GNOME
  2278. static char* one_format  = N_("%o %f \"%s\"%m");
  2279. static char* many_format = N_("%o %d %f%m");
  2280. #else
  2281. static char* one_format  = N_("%o %f \"%s\"%e");
  2282. static char* many_format = N_("%o %d %f%e");
  2283. #endif
  2284. static char* prompt_parts [] =
  2285. {
  2286.     N_("file"), N_("files"), N_("directory"), N_("directories"),
  2287.     N_("files/directories"), N_(" with source mask:"), N_(" to:")
  2288. };
  2289.  
  2290. char*
  2291. panel_operate_generate_prompt (char* cmd_buf, WPanel* panel, int operation, int only_one,
  2292.                    struct stat* src_stat)
  2293. {
  2294.     register char *sp, *cp;
  2295.     register int i;
  2296.     char format_string [200];
  2297.     char *dp = format_string;
  2298.     char* source = NULL;
  2299.  
  2300. #ifdef ENABLE_NLS
  2301.     static int i18n_flag = 0;
  2302.     if (!i18n_flag)
  2303.     {
  2304.         if (!fmd_i18n_flag)
  2305.             fmd_init_i18n(); /* to get proper fmd_xlen */
  2306.  
  2307.         for (i = sizeof (op_names1) / sizeof (op_names1 [0]); i--;)
  2308.             op_names1 [i] = _(op_names1 [i]);
  2309.  
  2310.         for (i = sizeof (prompt_parts) / sizeof (prompt_parts [0]); i--;)
  2311.             prompt_parts [i] = _(prompt_parts [i]);
  2312.  
  2313.         one_format = _(one_format);
  2314.         many_format = _(many_format);
  2315.         i18n_flag = 1;
  2316.     }
  2317. #endif /* ENABLE_NLS */
  2318.  
  2319.     sp = only_one ? one_format : many_format;
  2320.  
  2321.     if (only_one)
  2322.         source = panel_get_file (panel, src_stat);
  2323.  
  2324.     while (*sp)
  2325.     {
  2326.         switch (*sp)
  2327.         {
  2328.             case '%':
  2329.                 cp = NULL;
  2330.                 switch (sp[1])
  2331.                 {
  2332.                     case 'o':
  2333.                         cp = op_names1 [operation] + 1;
  2334.                         break;
  2335.                     case 'm':
  2336.                         cp = operation == OP_DELETE ? "?" : prompt_parts [5];
  2337.                         break;
  2338.                     case 'e':
  2339.                         cp = operation == OP_DELETE ? "?" : prompt_parts [6];
  2340.                         break;
  2341.                     case 'f':
  2342.                         if (only_one)
  2343.                         {
  2344.                             cp = S_ISDIR (src_stat->st_mode) ? 
  2345.                                 prompt_parts [2] : prompt_parts [0];
  2346.                         }
  2347.                         else
  2348.                         {
  2349.                             cp = (panel->marked == panel->dirs_marked) 
  2350.                             ? prompt_parts [3] 
  2351.                             : (panel->dirs_marked ? prompt_parts [4] 
  2352.                             : prompt_parts [1]);
  2353.                         }
  2354.                         break;
  2355.                     default:
  2356.                         *dp++ = *sp++;
  2357.                 }
  2358.                 if (cp)
  2359.                 {
  2360.                     sp += 2;
  2361.                     while (*cp)
  2362.                         *dp++ = *cp++;
  2363.                 }
  2364.                 break;
  2365.             default:
  2366.                 *dp++ = *sp++;
  2367.         }
  2368.     }
  2369.     *dp = '\0';
  2370.  
  2371.     if (only_one)
  2372.     {
  2373.         i = fmd_xlen - strlen(format_string) - 4;
  2374.         sprintf (cmd_buf, format_string, name_trunc (source, i));
  2375.     }
  2376.     else
  2377.     {
  2378.         sprintf (cmd_buf, format_string, panel->marked);
  2379.         i = strlen (cmd_buf) + 6 - fmd_xlen;
  2380.         if (i > 0)
  2381.         {
  2382.             fmd_xlen += i;
  2383.             fmd_init_i18n(); /* to recalculate positions of child widgets */
  2384.         }
  2385.     }
  2386.  
  2387.     return source;
  2388. }
  2389.  
  2390.  
  2391.  
  2392.