+ rprintf(FINFO, "[%s] flist start=%d, count=%d, low=%d, high=%d\n",
+ who, flist->ndx_start, flist->count, flist->low, flist->high);
+ for (i = 0; i < flist->count; i++) {
+ file = flist->sorted[i];
+ if ((am_root || am_sender) && preserve_uid) {
+ snprintf(uidbuf, sizeof uidbuf, " uid=%u",
+ F_OWNER(file));
+ } else
+ *uidbuf = '\0';
+ if (preserve_gid) {
+ static char parens[] = "(\0)\0\0\0";
+ char *pp = parens + (file->flags & FLAG_SKIP_GROUP ? 0 : 3);
+ snprintf(gidbuf, sizeof gidbuf, " gid=%s%u%s",
+ pp, F_GROUP(file), pp + 2);
+ } else
+ *gidbuf = '\0';
+ if (!am_sender)
+ snprintf(depthbuf, sizeof depthbuf, "%d", F_DEPTH(file));
+ if (F_IS_ACTIVE(file)) {
+ root = am_sender ? NS(F_PATHNAME(file)) : depthbuf;
+ if ((dir = file->dirname) == NULL)
+ dir = slash = "";
+ else
+ slash = "/";
+ name = file->basename;
+ trail = S_ISDIR(file->mode) ? "/" : "";
+ } else
+ root = dir = slash = name = trail = "";
+ rprintf(FINFO, "[%s] i=%d %s %s%s%s%s mode=0%o len=%.0f%s%s flags=%x\n",
+ who, i, root, dir, slash, name, trail, (int)file->mode,
+ (double)F_LENGTH(file), uidbuf, gidbuf, file->flags);
+ }
+}
+
+enum fnc_state { s_DIR, s_SLASH, s_BASE, s_TRAILING };
+enum fnc_type { t_PATH, t_ITEM };
+
+/* Compare the names of two file_struct entities, similar to how strcmp()
+ * would do if it were operating on the joined strings.
+ *
+ * Some differences beginning with protocol_version 29: (1) directory names
+ * are compared with an assumed trailing slash so that they compare in a
+ * way that would cause them to sort immediately prior to any content they
+ * may have; (2) a directory of any name compares after a non-directory of
+ * any name at the same depth; (3) a directory with name "." compares prior
+ * to anything else. These changes mean that a directory and a non-dir
+ * with the same name will not compare as equal (protocol_version >= 29).
+ *
+ * The dirname component can be an empty string, but the basename component
+ * cannot (and never is in the current codebase). The basename component
+ * may be NULL (for a removed item), in which case it is considered to be
+ * after any existing item. */
+int f_name_cmp(struct file_struct *f1, struct file_struct *f2)
+{
+ int dif;
+ const uchar *c1, *c2;
+ enum fnc_state state1, state2;
+ enum fnc_type type1, type2;
+ enum fnc_type t_path = protocol_version >= 29 ? t_PATH : t_ITEM;
+
+ if (!f1 || !F_IS_ACTIVE(f1)) {
+ if (!f2 || !F_IS_ACTIVE(f2))
+ return 0;
+ return -1;
+ }
+ if (!f2 || !F_IS_ACTIVE(f2))
+ return 1;