+ fbuf[ol] = '\0';
+ if (is_dot_dir)
+ fbuf[ol-1] = '.';
+ }
+}
+
+static int file_compare(const void *file1, const void *file2)
+{
+ return f_name_cmp(*(struct file_struct **)file1,
+ *(struct file_struct **)file2);
+}
+
+/* The guts of a merge-sort algorithm. This was derived from the glibc
+ * version, but I (Wayne) changed the merge code to do less copying and
+ * to require only half the amount of temporary memory. */
+static void fsort_tmp(struct file_struct **fp, size_t num,
+ struct file_struct **tmp)
+{
+ struct file_struct **f1, **f2, **t;
+ size_t n1, n2;
+
+ n1 = num / 2;
+ n2 = num - n1;
+ f1 = fp;
+ f2 = fp + n1;
+
+ if (n1 > 1)
+ fsort_tmp(f1, n1, tmp);
+ if (n2 > 1)
+ fsort_tmp(f2, n2, tmp);
+
+ while (f_name_cmp(*f1, *f2) <= 0) {
+ if (!--n1)
+ return;
+ f1++;
+ }
+
+ t = tmp;
+ memcpy(t, f1, n1 * PTR_SIZE);
+
+ *f1++ = *f2++, n2--;
+
+ while (n1 > 0 && n2 > 0) {
+ if (f_name_cmp(*t, *f2) <= 0)
+ *f1++ = *t++, n1--;
+ else
+ *f1++ = *f2++, n2--;
+ }
+
+ if (n1 > 0)
+ memcpy(f1, t, n1 * PTR_SIZE);
+}
+
+/* This file-struct sorting routine makes sure that any identical names in
+ * the file list stay in the same order as they were in the original list.
+ * This is particularly vital in inc_recurse mode where we expect a sort
+ * on the flist to match the exact order of a sort on the dir_flist. */
+static void fsort(struct file_struct **fp, size_t num)
+{
+ if (num <= 1)
+ return;
+
+ if (use_qsort)
+ qsort(fp, num, PTR_SIZE, file_compare);
+ else {
+ struct file_struct **tmp = new_array(struct file_struct *,
+ (num+1) / 2);
+ fsort_tmp(fp, num, tmp);
+ free(tmp);
+ }
+}
+
+/* We take an entire set of sibling dirs from the sorted flist and link them
+ * into the tree, setting the appropriate parent/child/sibling pointers. */
+static void add_dirs_to_tree(int parent_ndx, struct file_list *from_flist,
+ int dir_cnt)
+{
+ int i;
+ int32 *dp = NULL;
+ int32 *parent_dp = parent_ndx < 0 ? NULL
+ : F_DIR_NODE_P(dir_flist->sorted[parent_ndx]);
+
+ flist_expand(dir_flist, dir_cnt);
+ dir_flist->sorted = dir_flist->files;
+
+ for (i = 0; dir_cnt; i++) {
+ struct file_struct *file = from_flist->sorted[i];
+
+ if (!S_ISDIR(file->mode))
+ continue;
+
+ dir_flist->files[dir_flist->used++] = file;
+ dir_cnt--;
+
+ if (file->basename[0] == '.' && file->basename[1] == '\0')
+ continue;
+
+ if (dp)
+ DIR_NEXT_SIBLING(dp) = dir_flist->used - 1;
+ else if (parent_dp)
+ DIR_FIRST_CHILD(parent_dp) = dir_flist->used - 1;
+ else
+ send_dir_ndx = dir_flist->used - 1;
+
+ dp = F_DIR_NODE_P(file);
+ DIR_PARENT(dp) = parent_ndx;
+ DIR_FIRST_CHILD(dp) = -1;
+ }
+ if (dp)
+ DIR_NEXT_SIBLING(dp) = -1;
+}
+
+/* This function is normally called by the sender, but the receiving side also
+ * 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. */
+static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
+ int flags)
+{
+ struct dirent *di;
+ unsigned remainder;
+ char *p;
+ DIR *d;
+ int divert_dirs = (flags & FLAG_DIVERT_DIRS) != 0;
+ int start = flist->used;
+ int filter_level = f == -2 ? SERVER_FILTERS : ALL_FILTERS;
+
+ assert(flist != NULL);
+
+ if (!(d = opendir(fbuf))) {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "opendir %s failed", full_fname(fbuf));
+ return;
+ }
+
+ p = fbuf + len;
+ if (len != 1 || *fbuf != '/')
+ *p++ = '/';
+ *p = '\0';
+ remainder = MAXPATHLEN - (p - fbuf);
+
+ for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
+ char *dname = d_name(di);
+ if (dname[0] == '.' && (dname[1] == '\0'
+ || (dname[1] == '.' && dname[2] == '\0')))
+ continue;
+ if (strlcpy(p, dname, remainder) >= remainder) {
+ io_error |= IOERR_GENERAL;
+ rprintf(FINFO,
+ "cannot send long-named file %s\n",
+ full_fname(fbuf));
+ continue;
+ }
+ if (dname[0] == '\0') {
+ io_error |= IOERR_GENERAL;
+ rprintf(FINFO,
+ "cannot send file with empty name in %s\n",
+ full_fname(fbuf));
+ continue;
+ }
+
+ send_file_name(f, flist, fbuf, NULL, flags, filter_level);
+ }
+
+ fbuf[len] = '\0';
+
+ if (errno) {
+ io_error |= IOERR_GENERAL;
+ rsyserr(FERROR_XFER, errno, "readdir(%s)", full_fname(fbuf));
+ }
+
+ closedir(d);
+
+ if (f >= 0 && recurse && !divert_dirs) {
+ int i, end = flist->used - 1;
+ /* send_if_directory() bumps flist->used, so use "end". */
+ for (i = start; i <= end; i++)
+ send_if_directory(f, flist, flist->files[i], fbuf, len, flags);
+ }
+}
+
+static void send_implied_dirs(int f, struct file_list *flist, char *fname,
+ char *start, char *limit, int flags, char name_type)
+{
+ static char lastpath[MAXPATHLEN] = "";
+ static int lastpath_len = 0;
+ static struct file_struct *lastpath_struct = NULL;
+ struct file_struct *file;
+ item_list *relname_list;
+ relnamecache **rnpp;
+ int len, need_new_dir, depth = 0;
+ struct filter_list_struct save_filter_list = filter_list;
+
+ flags = (flags | FLAG_IMPLIED_DIR) & ~(FLAG_TOP_DIR | FLAG_CONTENT_DIR);
+ filter_list.head = filter_list.tail = NULL; /* Don't filter implied dirs. */
+
+ if (inc_recurse) {
+ if (lastpath_struct && F_PATHNAME(lastpath_struct) == pathname
+ && lastpath_len == limit - fname
+ && strncmp(lastpath, fname, lastpath_len) == 0)
+ need_new_dir = 0;
+ else
+ need_new_dir = 1;
+ } else {
+ char *tp = fname, *lp = lastpath;
+ /* Skip any initial directories in our path that we
+ * have in common with lastpath. */
+ assert(start == fname);
+ for ( ; ; tp++, lp++) {
+ if (tp == limit) {
+ if (*lp == '/' || *lp == '\0')
+ goto done;
+ break;
+ }
+ if (*lp != *tp)
+ break;
+ if (*tp == '/') {
+ start = tp;
+ depth++;
+ }
+ }
+ need_new_dir = 1;