+static void output_flist(struct file_list *flist, const char *whose_list)
+{
+ char uidbuf[16], gidbuf[16];
+ struct file_struct *file;
+ int i;
+
+ for (i = 0; i < flist->count; i++) {
+ file = flist->files[i];
+ if ((am_root || am_sender) && preserve_uid)
+ sprintf(uidbuf, " uid=%ld", (long)file->uid);
+ else
+ *uidbuf = '\0';
+ if (preserve_gid && file->gid != GID_NONE)
+ sprintf(gidbuf, " gid=%ld", (long)file->gid);
+ else
+ *gidbuf = '\0';
+ rprintf(FINFO, "[%s] i=%d %s %s %s mode=0%o len=%.0f%s%s\n",
+ whose_list, i, NS(file->basedir), NS(file->dirname),
+ NS(file->basename), (int)file->mode,
+ (double)file->length, uidbuf, gidbuf);
+ }
+}
+
+
+enum fnc_state { fnc_DIR, fnc_SLASH, fnc_BASE };
+
+/* Compare the names of two file_struct entities, just like strcmp()
+ * would do if it were operating on the joined strings. We assume
+ * that there are no 0-length strings.
+ */
+int f_name_cmp(struct file_struct *f1, struct file_struct *f2)
+{
+ int dif;
+ const uchar *c1, *c2;
+ enum fnc_state state1, state2;
+
+ if (!f1 || !f1->basename) {
+ if (!f2 || !f2->basename)
+ return 0;
+ return -1;
+ }
+ if (!f2 || !f2->basename)
+ return 1;
+
+ if (!(c1 = (uchar*)f1->dirname)) {
+ state1 = fnc_BASE;
+ c1 = (uchar*)f1->basename;
+ } else if (!*c1) {
+ state1 = fnc_SLASH;
+ c1 = (uchar*)"/";
+ } else
+ state1 = fnc_DIR;
+ if (!(c2 = (uchar*)f2->dirname)) {
+ state2 = fnc_BASE;
+ c2 = (uchar*)f2->basename;
+ } else if (!*c2) {
+ state2 = fnc_SLASH;
+ c2 = (uchar*)"/";
+ } else
+ state2 = fnc_DIR;
+
+ while (1) {
+ if ((dif = (int)*c1 - (int)*c2) != 0)
+ break;
+ if (!*++c1) {
+ switch (state1) {
+ case fnc_DIR:
+ state1 = fnc_SLASH;
+ c1 = (uchar*)"/";
+ break;
+ case fnc_SLASH:
+ state1 = fnc_BASE;
+ c1 = (uchar*)f1->basename;
+ break;
+ case fnc_BASE:
+ break;
+ }
+ }
+ if (!*++c2) {
+ switch (state2) {
+ case fnc_DIR:
+ state2 = fnc_SLASH;
+ c2 = (uchar*)"/";
+ break;
+ case fnc_SLASH:
+ state2 = fnc_BASE;
+ c2 = (uchar*)f2->basename;
+ break;
+ case fnc_BASE:
+ if (!*c1)
+ return 0;
+ break;
+ }
+ }
+ }
+
+ return dif;
+}
+
+
+/* Return a copy of the full filename of a flist entry, using the indicated
+ * buffer. No size-checking is done because we checked the size when creating
+ * the file_struct entry.
+ */
+char *f_name_to(struct file_struct *f, char *fbuf)
+{
+ if (!f || !f->basename)
+ return NULL;
+
+ if (f->dirname) {
+ int len = strlen(f->dirname);
+ memcpy(fbuf, f->dirname, len);
+ fbuf[len] = '/';
+ strcpy(fbuf + len + 1, f->basename);
+ } else
+ strcpy(fbuf, f->basename);
+ return fbuf;
+}
+
+
+/* Like f_name_to(), but we rotate through 5 static buffers of our own.
+ */
+char *f_name(struct file_struct *f)
+{
+ static char names[5][MAXPATHLEN];
+ static unsigned int n;
+
+ n = (n + 1) % (sizeof names / sizeof names[0]);
+
+ return f_name_to(f, names[n]);
+}
+
+static int is_backup_file(char *fn)
+{
+ int k = strlen(fn) - backup_suffix_len;
+ return k > 0 && strcmp(fn+k, backup_suffix) == 0;
+}
+
+void delete_in_dir(struct file_list *flist, char *fname)
+{
+ static int deletion_count = 0;
+ struct file_list *del_flist;
+ int save_recurse = recurse;
+ int save_xfer_dirs = xfer_dirs;
+ int save_implied_dirs = implied_dirs;
+ int save_relative_paths = relative_paths;
+ char *argv[1];
+ int i, j, mode;
+
+ if (max_delete && deletion_count >= max_delete)
+ return;
+
+ if (io_error && !(lp_ignore_errors(module_id) || ignore_errors)) {
+ rprintf(FINFO, "IO error encountered - skipping file deletion\n");
+ max_delete = -1; /* avoid duplicating the above warning */
+ return;
+ }
+
+ if (delete_during) {
+ recurse = 1; /* allow one level only */
+ xfer_dirs = 1;
+ implied_dirs = 0;
+ relative_paths = 1;
+ }
+
+ argv[0] = fname;
+ del_flist = send_file_list(-1, 1, argv);
+
+ relative_paths = save_relative_paths;
+ implied_dirs = save_implied_dirs;
+ xfer_dirs = save_xfer_dirs;
+ recurse = save_recurse;
+
+ if (!del_flist)
+ return;
+
+ if (verbose > 1)
+ rprintf(FINFO, "deleting in %s\n", safe_fname(fname));
+
+ for (i = del_flist->count-1; i >= 0; i--) {
+ if (max_delete && deletion_count >= max_delete)
+ break;
+ if (!del_flist->files[i]->basename)
+ continue;
+ mode = del_flist->files[i]->mode;
+ if ((j = flist_find(flist, del_flist->files[i])) < 0
+ || (delete_during && S_ISDIR(mode)
+ && !S_ISDIR(flist->files[j]->mode))) {
+ char *f = f_name(del_flist->files[i]);
+ if (make_backups && (backup_dir || !is_backup_file(f))
+ && !S_ISDIR(mode)) {
+ make_backup(f);
+ if (verbose) {
+ rprintf(FINFO, "deleting %s\n",
+ safe_fname(f));
+ }
+ } else {
+ int dflags = delete_during
+ ? DEL_DIR | DEL_FORCE_RECURSE
+ : DEL_DIR | DEL_NO_RECURSE;
+ delete_file(f, S_ISDIR(mode) ? dflags : 0);
+ }
+ deletion_count++;
+ }
+ }
+ flist_free(del_flist);
+}