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