- for (i=0;i<flist->count;i++) {
- rprintf(FINFO,"[%d] i=%d %s %s mode=0%o len=%d\n",
- getpid(), i,
- NS(flist->files[i]->dirname),
- NS(flist->files[i]->basename),
- flist->files[i]->mode,
- (int)flist->files[i]->length);
+enum fnc_state { s_DIR, s_SLASH, s_BASE, s_TRAILING };
+
+/* Compare the names of two file_struct entities, similar to how strcmp()
+ * would do if it were operating on the joined strings. The only difference
+ * is that, beginning with protocol_version 29, a directory name will always
+ * sort immediately prior to its contents (previously "foo." would sort in
+ * between directory "foo" and "foo/bar"). We do this by assuming that a dir
+ * has a trailing slash for comparison purposes, but only if we aren't about
+ * to match a file of the same name (because we need all identically named
+ * items to match each other). 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, in which case it is sorted to the end
+ * of the list (as a removed item). */
+int f_name_cmp(struct file_struct *f1, struct file_struct *f2)
+{
+ int dif;
+ const uchar *c1, *c2;
+ enum fnc_state state1, state2;
+
+ 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) {
+ state1 = s_BASE;
+ c1 = (uchar*)f1->basename;
+ } else if (!*c1) {
+ state1 = s_SLASH;
+ c1 = (uchar*)"/";
+ } else
+ state1 = s_DIR;
+ if (!c2) {
+ state2 = s_BASE;
+ c2 = (uchar*)f2->basename;
+ } else if (!*c2) {
+ state2 = s_SLASH;
+ c2 = (uchar*)"/";
+ } else
+ state2 = s_DIR;
+
+ 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:
+ state1 = s_BASE;
+ c1 = (uchar*)f1->basename;
+ break;
+ case s_BASE:
+ state1 = s_TRAILING;
+ if (protocol_version >= 29 && S_ISDIR(f1->mode))
+ c1 = (uchar*)"/";
+ break;
+ case s_TRAILING:
+ break;
+ }
+ }
+ if (!*++c2) {
+ switch (state2) {
+ case s_DIR:
+ state2 = s_SLASH;
+ c2 = (uchar*)"/";
+ break;
+ case s_SLASH:
+ state2 = s_BASE;
+ c2 = (uchar*)f2->basename;
+ break;
+ case s_BASE:
+ if (state1 == s_TRAILING)
+ return 0;
+ state2 = s_TRAILING;
+ if (protocol_version >= 29 && S_ISDIR(f2->mode))
+ c2 = (uchar*)"/";
+ break;
+ case s_TRAILING:
+ break;
+ }
+ }