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
/* 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. */
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] == '/');
}
/* 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);
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;
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;