Mention the EEXIST fix.
[rsync.git] / flist.c
diff --git a/flist.c b/flist.c
index b611373..64ff007 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -655,6 +655,9 @@ static struct file_struct *receive_file_entry(struct file_list *flist,
                if (flags & XMIT_TOP_DIR) {
                        in_del_hier = 1;
                        del_hier_name_len = file->dir.depth == 0 ? 0 : l1 + l2;
+                       if (relative_paths && del_hier_name_len > 2
+                           && basename_len == 1+1 && *basename == '.')
+                               del_hier_name_len -= 2;
                        file->flags |= FLAG_TOP_DIR | FLAG_DEL_HERE;
                } else if (in_del_hier) {
                        if (!relative_paths || !del_hier_name_len
@@ -1008,7 +1011,7 @@ static void send_if_directory(int f, struct file_list *flist,
 
 
 /* This function is normally called by the sender, but the receiving side also
- * calls it from delete_in_dir() with f set to -1 so that we just construct the
+ * calls it from get_dirlist() with f set to -1 so that we just construct the
  * file list in memory without sending it over the wire.  Also, get_dirlist()
  * might call this with f set to -2, which also indicates that local filter
  * rules should be ignored. */
@@ -1117,11 +1120,21 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                        if (l == 2 && fname[0] == '.') {
                                /* Turn "./" into just "." rather than "./." */
                                fname[1] = '\0';
-                       } else if (l < MAXPATHLEN) {
+                       } else {
+                               if (l + 1 >= MAXPATHLEN)
+                                       overflow("send_file_list");
                                fname[l++] = '.';
                                fname[l] = '\0';
                        }
                        is_dot_dir = 1;
+               } else if (l > 1 && fname[l-1] == '.' && fname[l-2] == '.'
+                   && (l == 2 || fname[l-3] == '/')) {
+                       if (l + 2 >= MAXPATHLEN)
+                               overflow("send_file_list");
+                       fname[l++] = '/';
+                       fname[l++] = '.';
+                       fname[l] = '\0';
+                       is_dot_dir = 1;
                } else {
                        is_dot_dir = fname[l-1] == '.'
                                   && (l == 1 || fname[l-2] == '/');
@@ -1458,8 +1471,12 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
 {
        int i, prev_i = 0;
 
-       if (!flist || flist->count == 0)
+       if (!flist)
                return;
+       if (flist->count == 0) {
+               flist->high = -1;
+               return;
+       }
 
        sorting_flist = flist;
        qsort(flist->files, flist->count,
@@ -1511,8 +1528,8 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
                        }
                        /* Make sure that if we unduplicate '.', that we don't
                         * lose track of a user-specified top directory. */
-                       if (flist->files[drop]->flags & FLAG_TOP_DIR)
-                               flist->files[keep]->flags |= FLAG_TOP_DIR;
+                       flist->files[keep]->flags |= flist->files[drop]->flags
+                                                  & (FLAG_TOP_DIR|FLAG_DEL_HERE);
 
                        clear_file(drop, flist);
 
@@ -1667,8 +1684,13 @@ int f_name_cmp(struct file_struct *f1, struct file_struct *f2)
                                break;
                        case s_SLASH:
                                type1 = S_ISDIR(f1->mode) ? t_path : t_ITEM;
-                               state1 = s_BASE;
                                c1 = (uchar*)f1->basename;
+                               if (type1 == t_PATH && *c1 == '.' && !c1[1]) {
+                                       type1 = t_ITEM;
+                                       state1 = s_TRAILING;
+                                       c1 = (uchar*)"";
+                               } else
+                                       state1 = s_BASE;
                                break;
                        case s_BASE:
                                state1 = s_TRAILING;
@@ -1692,8 +1714,13 @@ int f_name_cmp(struct file_struct *f1, struct file_struct *f2)
                                break;
                        case s_SLASH:
                                type2 = S_ISDIR(f2->mode) ? t_path : t_ITEM;
-                               state2 = s_BASE;
                                c2 = (uchar*)f2->basename;
+                               if (type2 == t_PATH && *c2 == '.' && !c2[1]) {
+                                       type2 = t_ITEM;
+                                       state2 = s_TRAILING;
+                                       c2 = (uchar*)"";
+                               } else
+                                       state2 = s_BASE;
                                break;
                        case s_BASE:
                                state2 = s_TRAILING;