-void clean_flist(struct file_list *flist)
-{
- int i;
-
- if (!flist || flist->count == 0)
- return;
-
- for (i=0;i<flist->count;i++) {
- clean_fname(flist->files[i].name);
- }
-
- qsort(flist->files,flist->count,
- sizeof(flist->files[0]),
- (int (*)())file_compare);
-
- for (i=1;i<flist->count;i++) {
- if (flist->files[i].name &&
- strcmp(flist->files[i].name,flist->files[i-1].name) == 0) {
- if (verbose > 1 && !am_server)
- fprintf(FERROR,"removing duplicate name %s from file list %d\n",
- flist->files[i-1].name,i-1);
- free(flist->files[i-1].name);
- bzero((char *)&flist->files[i-1],sizeof(flist->files[i-1]));
- }
- }
+static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
+{
+ int i, prev_i = 0;
+
+ if (!flist || flist->count == 0)
+ return;
+
+ qsort(flist->files, flist->count,
+ sizeof(flist->files[0]), (int (*)()) file_compare);
+
+ for (i = no_dups? 0 : flist->count; i < flist->count; i++) {
+ if (flist->files[i]->basename) {
+ prev_i = i;
+ break;
+ }
+ }
+ while (++i < flist->count) {
+ if (!flist->files[i]->basename)
+ continue;
+ if (f_name_cmp(flist->files[i], flist->files[prev_i]) == 0) {
+ if (verbose > 1 && !am_server) {
+ rprintf(FINFO,
+ "removing duplicate name %s from file list %d\n",
+ f_name(flist->files[i]), i);
+ }
+ /* Make sure that if we unduplicate '.', that we don't
+ * lose track of a user-specified starting point (or
+ * else deletions will mysteriously fail with -R). */
+ if (flist->files[i]->flags & FLAG_DELETE)
+ flist->files[prev_i]->flags |= FLAG_DELETE;
+ /* it's not great that the flist knows the semantics of
+ * the file memory usage, but i'd rather not add a flag
+ * byte to that struct.
+ * XXX can i use a bit in the flags field? */
+ if (flist->string_area)
+ flist->files[i][0] = null_file;
+ else
+ free_file(flist->files[i]);
+ } else
+ prev_i = i;
+ }
+
+ if (strip_root) {
+ /* we need to strip off the root directory in the case
+ of relative paths, but this must be done _after_
+ the sorting phase */
+ for (i = 0; i < flist->count; i++) {
+ if (flist->files[i]->dirname &&
+ flist->files[i]->dirname[0] == '/') {
+ memmove(&flist->files[i]->dirname[0],
+ &flist->files[i]->dirname[1],
+ strlen(flist->files[i]->dirname));
+ }
+
+ if (flist->files[i]->dirname &&
+ !flist->files[i]->dirname[0]) {
+ flist->files[i]->dirname = NULL;
+ }
+ }
+ }
+
+ if (verbose <= 3)
+ return;
+
+ for (i = 0; i < flist->count; i++) {
+ rprintf(FINFO, "[%ld] i=%d %s %s mode=0%o len=%.0f\n",
+ (long) getpid(), i,
+ NS(flist->files[i]->dirname),
+ NS(flist->files[i]->basename),
+ (int) flist->files[i]->mode,
+ (double) flist->files[i]->length);
+ }
+}
+
+
+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
+ state1 = fnc_DIR;
+ if (!(c2 = (uchar*)f2->dirname)) {
+ state2 = fnc_BASE;
+ c2 = (uchar*)f2->basename;
+ } 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;