- for (i=0;i<flist->count;i++) {
- rprintf(FINFO,"[%d] i=%d %s %s mode=0%o len=%.0f\n",
- (int) getpid(), i,
- NS(flist->files[i]->dirname),
- NS(flist->files[i]->basename),
- (int) flist->files[i]->mode,
- (double)flist->files[i]->length);
+/* 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 || !f1->basename) {
+ if (!f2 || !f2->basename)
+ return 0;
+ return -1;
+ }
+ if (!f2 || !f2->basename)
+ return 1;
+
+ c1 = (uchar*)f1->dirname;
+ c2 = (uchar*)f2->dirname;
+ if (c1 == c2)
+ c1 = c2 = NULL;
+ if (!c1) {
+ type1 = S_ISDIR(f1->mode) ? t_path : t_ITEM;
+ c1 = (uchar*)f1->basename;
+ if (type1 == t_PATH && *c1 == '.' && !c1[1]) {
+ type1 = t_ITEM;
+ state1 = s_TRAILING;
+ c1 = (uchar*)"";
+ } else
+ state1 = s_BASE;
+ } else if (!*c1) {
+ type1 = t_path;
+ state1 = s_SLASH;
+ c1 = (uchar*)"/";
+ } else {
+ type1 = t_path;
+ state1 = s_DIR;
+ }
+ if (!c2) {
+ type2 = S_ISDIR(f2->mode) ? t_path : t_ITEM;
+ c2 = (uchar*)f2->basename;
+ if (type2 == t_PATH && *c2 == '.' && !c2[1]) {
+ type2 = t_ITEM;
+ state2 = s_TRAILING;
+ c2 = (uchar*)"";
+ } else
+ state2 = s_BASE;
+ } else if (!*c2) {
+ type2 = t_path;
+ state2 = s_SLASH;
+ c2 = (uchar*)"/";
+ } else {
+ type2 = t_path;
+ state2 = s_DIR;
+ }
+
+ if (type1 != type2)
+ return type1 == t_PATH ? 1 : -1;
+
+ while (1) {
+ if ((dif = (int)*c1++ - (int)*c2++) != 0)
+ break;
+ if (!*c1) {
+ switch (state1) {
+ case s_DIR:
+ state1 = s_SLASH;
+ c1 = (uchar*)"/";
+ break;
+ case s_SLASH:
+ type1 = S_ISDIR(f1->mode) ? t_path : t_ITEM;
+ state1 = s_BASE;
+ c1 = (uchar*)f1->basename;
+ break;
+ case s_BASE:
+ state1 = s_TRAILING;
+ if (type1 == t_PATH) {
+ c1 = (uchar*)"/";
+ break;
+ }
+ /* FALL THROUGH */
+ case s_TRAILING:
+ type1 = t_ITEM;
+ break;
+ }
+ if (*c2 && type1 != type2)
+ return type1 == t_PATH ? 1 : -1;
+ }
+ if (!*c2) {
+ switch (state2) {
+ case s_DIR:
+ state2 = s_SLASH;
+ c2 = (uchar*)"/";
+ break;
+ case s_SLASH:
+ type2 = S_ISDIR(f2->mode) ? t_path : t_ITEM;
+ state2 = s_BASE;
+ c2 = (uchar*)f2->basename;
+ break;
+ case s_BASE:
+ state2 = s_TRAILING;
+ if (type2 == t_PATH) {
+ c2 = (uchar*)"/";
+ break;
+ }
+ /* FALL THROUGH */
+ case s_TRAILING:
+ if (!*c1)
+ return 0;
+ type2 = t_ITEM;
+ break;
+ }
+ if (type1 != type2)
+ return type1 == t_PATH ? 1 : -1;
+ }