Updated to apply to latest source.
[rsync-patches.git] / fuzzy.diff
1 Depends-On-Patch: g2r-basis-filename.diff
2
3 The changes to generator.c were greatly simplified, making the patch
4 easier to maintain and fixing the failing test in the testsuite.
5 Very lightly tested.
6
7 Be sure to run "make proto" before "make".
8
9 --- orig/generator.c    2004-07-03 20:08:07
10 +++ generator.c 2004-07-03 20:09:05
11 @@ -41,6 +41,7 @@ extern int ignore_times;
12  extern int size_only;
13  extern int io_timeout;
14  extern int protocol_version;
15 +extern int fuzzy;
16  extern int always_checksum;
17  extern char *compare_dest;
18  extern int link_dest;
19 @@ -238,6 +239,94 @@ static void generate_and_send_sums(struc
20  }
21  
22  
23 +static void split_names(char *fname, char **dirname, char **basename)
24 +{
25 +       char *slash = strrchr(fname, '/');
26 +       if (slash) {
27 +               *dirname = fname;
28 +               *slash = '\0';
29 +               *basename = slash+1;
30 +       } else {
31 +               *basename = fname;
32 +               *dirname = ".";
33 +       }
34 +}
35 +
36 +
37 +static unsigned int measure_name(const char *name, const char *basename,
38 +                                const char *ext)
39 +{
40 +       int namelen = strlen(name);
41 +       int extlen = strlen(ext);
42 +       unsigned int score = 0;
43 +
44 +       /* Extensions must match */
45 +       if (namelen <= extlen || strcmp(name + namelen - extlen, ext) != 0)
46 +               return 0;
47 +
48 +       /* Now score depends on similarity of prefix */
49 +       for (; *name == *basename && *name; name++, basename++)
50 +               score++;
51 +       return score;
52 +}
53 +
54 +
55 +static int find_fuzzy(char **fname_ptr, char *buf, STRUCT_STAT *st_ptr)
56 +{
57 +       DIR *d;
58 +       struct dirent *di;
59 +       char *basename, *dirname;
60 +       char mangled_name[MAXPATHLEN];
61 +       char bestname[MAXPATHLEN];
62 +       unsigned int bestscore = 0;
63 +       const char *ext;
64 +
65 +       strlcpy(mangled_name, *fname_ptr, sizeof mangled_name);
66 +
67 +       split_names(mangled_name, &dirname, &basename);
68 +       if (!(d = opendir(dirname))) {
69 +               rsyserr(FERROR, errno, "recv_generator opendir(%s)", dirname);
70 +               return -1;
71 +       }
72 +
73 +       /* Get final extension, eg. .gz; never full basename though. */
74 +       ext = strrchr(basename + 1, '.');
75 +       if (!ext)
76 +               ext = basename + strlen(basename); /* ext = "" */
77 +
78 +       while ((di = readdir(d)) != NULL) {
79 +               const char *dname = d_name(di);
80 +               unsigned int score;
81 +
82 +               if (dname[0] == '.' && (dname[1] == '\0'
83 +                   || (dname[1] == '.' && dname[2] == '\0')))
84 +                       continue;
85 +
86 +               score = measure_name(dname, basename, ext);
87 +               if (verbose > 4) {
88 +                       rprintf(FINFO, "[%s] fuzzy score for %s = %u\n",
89 +                               who_am_i(), dname, score);
90 +               }
91 +               if (score > bestscore) {
92 +                       strlcpy(bestname, dname, sizeof bestname);
93 +                       bestscore = score;
94 +               }
95 +       }
96 +       closedir(d);
97 +
98 +       /* Found a candidate. */
99 +       if (bestscore != 0) {
100 +               pathjoin(buf, MAXPATHLEN, dirname, bestname);
101 +               if (verbose > 2) {
102 +                       rprintf(FINFO, "[%s] fuzzy match %s->%s\n",
103 +                               who_am_i(), *fname_ptr, buf);
104 +               }
105 +               *fname_ptr = buf;
106 +               return link_stat(buf, st_ptr, 0);
107 +       }
108 +       return -1;
109 +}
110 +
111  
112  /*
113   * Acts on file number @p i from @p flist, whose name is @p fname.
114 @@ -253,7 +342,7 @@ static void recv_generator(char *fname, 
115         int fd;
116         STRUCT_STAT st;
117         struct map_struct *mapbuf;
118 -       int statret;
119 +       int statret, fuzzy_file = 0;
120         char *fnamecmp;
121         char fnamecmpbuf[MAXPATHLEN];
122  
123 @@ -436,6 +525,14 @@ static void recv_generator(char *fname, 
124         } else
125                 *fnamecmpbuf = '\0';
126  
127 +       if (statret == -1 && fuzzy) {
128 +               statret = find_fuzzy(&fnamecmp, fnamecmpbuf, &st);
129 +               if (!S_ISREG(st.st_mode))
130 +                       statret = -1;
131 +               else
132 +                       fuzzy_file = 1;
133 +       }
134 +
135         if (statret == -1) {
136                 if (preserve_hard_links && hard_link_check(file, HL_SKIP))
137                         return;
138 @@ -482,7 +579,7 @@ static void recv_generator(char *fname, 
139                 return;
140         }
141  
142 -       if (skip_file(fname, file, &st)) {
143 +       if (!fuzzy_file && skip_file(fname, file, &st)) {
144                 if (!*fnamecmpbuf)
145                         set_perms(fname, file, &st, PERMS_REPORT);
146                 return;
147 --- orig/options.c      2004-07-15 16:51:50
148 +++ options.c   2004-07-03 19:27:25
149 @@ -94,6 +94,7 @@ int ignore_errors = 0;
150  int modify_window = 0;
151  int blocking_io = -1;
152  int checksum_seed = 0;
153 +int fuzzy = 0;
154  unsigned int block_size = 0;
155  
156  
157 @@ -269,6 +270,7 @@ void usage(enum logcode F)
158    rprintf(F," -T  --temp-dir=DIR          create temporary files in directory DIR\n");
159    rprintf(F,"     --compare-dest=DIR      also compare destination files relative to DIR\n");
160    rprintf(F,"     --link-dest=DIR         create hardlinks to DIR for unchanged files\n");
161 +  rprintf(F,"     --fuzzy                 use similar file as basis if basis doesn't exist\n");
162    rprintf(F," -P                          equivalent to --partial --progress\n");
163    rprintf(F," -z, --compress              compress file data\n");
164    rprintf(F," -C, --cvs-exclude           auto ignore files in the same way CVS does\n");
165 @@ -367,6 +369,7 @@ static struct poptOption long_options[] 
166    {"temp-dir",        'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
167    {"compare-dest",     0,  POPT_ARG_STRING, &compare_dest, 0, 0, 0 },
168    {"link-dest",        0,  POPT_ARG_STRING, &compare_dest,  OPT_LINK_DEST, 0, 0 },
169 +  {"fuzzy",            0,  POPT_ARG_NONE,   &fuzzy, 0, 0, 0 },
170    /* TODO: Should this take an optional int giving the compression level? */
171    {"compress",        'z', POPT_ARG_NONE,   &do_compression, 0, 0, 0 },
172    {"daemon",           0,  POPT_ARG_NONE,   &daemon_opt, 0, 0, 0 },
173 @@ -981,6 +984,9 @@ void server_options(char **args,int *arg
174                 }
175         }
176  
177 +       if (fuzzy && am_sender)
178 +               args[ac++] = "--fuzzy";
179 +
180         *argc = ac;
181         return;
182  
183 --- orig/receiver.c     2004-07-03 20:08:07
184 +++ receiver.c  2004-07-03 20:09:05
185 @@ -36,7 +36,6 @@ extern int preserve_perms;
186  extern int cvs_exclude;
187  extern int io_error;
188  extern char *tmpdir;
189 -extern char *compare_dest;
190  extern int make_backups;
191  extern int do_progress;
192  extern char *backup_dir;
193 --- orig/rsync.yo       2004-07-15 02:21:11
194 +++ rsync.yo    2004-07-03 19:27:25
195 @@ -325,6 +325,7 @@ verb(
196   -T  --temp-dir=DIR          create temporary files in directory DIR
197       --compare-dest=DIR      also compare received files relative to DIR
198       --link-dest=DIR         create hardlinks to DIR for unchanged files
199 +     --fuzzy                 use similar file as basis if basis is gone
200   -P                          equivalent to --partial --progress
201   -z, --compress              compress file data
202   -C, --cvs-exclude           auto ignore files in the same way CVS does