1 /* Read, sort and compare two directories. Used for GNU DIFF. 2 3 Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006-2007, 4 2009-2011 Free Software Foundation, Inc. 5 6 This file is part of GNU DIFF. 7 8 This program is free software: you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation, either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 20 21 #include "diff.h" 22 #include <error.h> 23 #include <exclude.h> 24 #include <filenamecat.h> 25 #include <setjmp.h> 26 #include <xalloc.h> 27 28 /* Read the directory named by DIR and store into DIRDATA a sorted vector 29 of filenames for its contents. DIR->desc == -1 means this directory is 30 known to be nonexistent, so set DIRDATA to an empty vector. 31 Return -1 (setting errno) if error, 0 otherwise. */ 32 33 struct dirdata 34 { 35 size_t nnames; /* Number of names. */ 36 char const **names; /* Sorted names of files in dir, followed by 0. */ 37 char *data; /* Allocated storage for file names. */ 38 }; 39 40 /* Whether file names in directories should be compared with 41 locale-specific sorting. */ 42 static bool locale_specific_sorting; 43 44 /* Where to go if locale-specific sorting fails. */ 45 static jmp_buf failed_locale_specific_sorting; 46 47 static bool dir_loop (struct comparison const *, int); 48 static int compare_names_for_qsort (void const *, void const *); 49 50 51 /* Read a directory and get its vector of names. */ 52 53 static bool 54 dir_read (struct file_data const *dir, struct dirdata *dirdata) 55 { 56 register struct dirent *next; 57 register size_t i; 58 59 /* Address of block containing the files that are described. */ 60 char const **names; 61 62 /* Number of files in directory. */ 63 size_t nnames; 64 65 /* Allocated and used storage for file name data. */ 66 char *data; 67 size_t data_alloc, data_used; 68 69 dirdata->names = 0; 70 dirdata->data = 0; 71 nnames = 0; 72 data = 0; 73 74 if (dir->desc != -1) 75 { 76 /* Open the directory and check for errors. */ 77 register DIR *reading = opendir (dir->name); 78 if (!reading) 79 return false; 80 81 /* Initialize the table of filenames. */ 82 83 data_alloc = 512; 84 data_used = 0; 85 dirdata->data = data = xmalloc (data_alloc); 86 87 /* Read the directory entries, and insert the subfiles 88 into the `data' table. */ 89 90 while ((errno = 0, (next = readdir (reading)) != 0)) 91 { 92 char *d_name = next->d_name; 93 size_t d_size = _D_EXACT_NAMLEN (next) + 1; 94 95 /* Ignore "." and "..". */ 96 if (d_name[0] == '.' 97 && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0))) 98 continue; 99 100 if (excluded_file_name (excluded, d_name)) 101 continue; 102 103 while (data_alloc < data_used + d_size) 104 { 105 if (PTRDIFF_MAX / 2 <= data_alloc) 106 xalloc_die (); 107 dirdata->data = data = xrealloc (data, data_alloc *= 2); 108 } 109 110 memcpy (data + data_used, d_name, d_size); 111 data_used += d_size; 112 nnames++; 113 } 114 if (errno) 115 { 116 int e = errno; 117 closedir (reading); 118 errno = e; 119 return false; 120 } 121 #if CLOSEDIR_VOID 122 closedir (reading); 123 #else 124 if (closedir (reading) != 0) 125 return false; 126 #endif 127 } 128 129 /* Create the `names' table from the `data' table. */ 130 if (PTRDIFF_MAX / sizeof *names - 1 <= nnames) 131 xalloc_die (); 132 dirdata->names = names = xmalloc ((nnames + 1) * sizeof *names); 133 dirdata->nnames = nnames; 134 for (i = 0; i < nnames; i++) 135 { 136 names[i] = data; 137 data += strlen (data) + 1; 138 } 139 names[nnames] = 0; 140 return true; 141 } 142 143 /* Compare file names, returning a value compatible with strcmp. */ 144 145 static int 146 compare_names (char const *name1, char const *name2) 147 { 148 if (locale_specific_sorting) 149 { 150 int r; 151 errno = 0; 152 if (ignore_file_name_case) 153 r = strcasecoll (name1, name2); 154 else 155 r = strcoll (name1, name2); 156 if (errno) 157 { 158 error (0, errno, _("cannot compare file names `%s' and `%s'"), 159 name1, name2); 160 longjmp (failed_locale_specific_sorting, 1); 161 } 162 return r; 163 } 164 165 return file_name_cmp (name1, name2); 166 } 167 168 /* Compare names FILE1 and FILE2 when sorting a directory. 169 Prefer filtered comparison, breaking ties with file_name_cmp. */ 170 171 static int 172 compare_names_for_qsort (void const *file1, void const *file2) 173 { 174 char const *const *f1 = file1; 175 char const *const *f2 = file2; 176 int diff = compare_names (*f1, *f2); 177 return diff ? diff : file_name_cmp (*f1, *f2); 178 } 179 180 /* Compare the contents of two directories named in CMP. 181 This is a top-level routine; it does everything necessary for diff 182 on two directories. 183 184 CMP->file[0].desc == -1 says directory CMP->file[0] doesn't exist, 185 but pretend it is empty. Likewise for CMP->file[1]. 186 187 HANDLE_FILE is a caller-provided subroutine called to handle each file. 188 It gets three operands: CMP, name of file in dir 0, name of file in dir 1. 189 These names are relative to the original working directory. 190 191 For a file that appears in only one of the dirs, one of the name-args 192 to HANDLE_FILE is zero. 193 194 Returns the maximum of all the values returned by HANDLE_FILE, 195 or EXIT_TROUBLE if trouble is encountered in opening files. */ 196 197 int 198 diff_dirs (struct comparison const *cmp, 199 int (*handle_file) (struct comparison const *, 200 char const *, char const *)) 201 { 202 struct dirdata dirdata[2]; 203 int volatile val = EXIT_SUCCESS; 204 int i; 205 206 if ((cmp->file[0].desc == -1 || dir_loop (cmp, 0)) 207 && (cmp->file[1].desc == -1 || dir_loop (cmp, 1))) 208 { 209 error (0, 0, _("%s: recursive directory loop"), 210 cmp->file[cmp->file[0].desc == -1].name); 211 return EXIT_TROUBLE; 212 } 213 214 /* Get contents of both dirs. */ 215 for (i = 0; i < 2; i++) 216 if (! dir_read (&cmp->file[i], &dirdata[i])) 217 { 218 perror_with_name (cmp->file[i].name); 219 val = EXIT_TROUBLE; 220 } 221 222 if (val == EXIT_SUCCESS) 223 { 224 char const **volatile names[2]; 225 names[0] = dirdata[0].names; 226 names[1] = dirdata[1].names; 227 228 /* Use locale-specific sorting if possible, else native byte order. */ 229 locale_specific_sorting = true; 230 if (setjmp (failed_locale_specific_sorting)) 231 locale_specific_sorting = false; 232 233 /* Sort the directories. */ 234 for (i = 0; i < 2; i++) 235 qsort (names[i], dirdata[i].nnames, sizeof *dirdata[i].names, 236 compare_names_for_qsort); 237 238 /* If `-S name' was given, and this is the topmost level of comparison, 239 ignore all file names less than the specified starting name. */ 240 241 if (starting_file && ! cmp->parent) 242 { 243 while (*names[0] && compare_names (*names[0], starting_file) < 0) 244 names[0]++; 245 while (*names[1] && compare_names (*names[1], starting_file) < 0) 246 names[1]++; 247 } 248 249 /* Loop while files remain in one or both dirs. */ 250 while (*names[0] || *names[1]) 251 { 252 /* Compare next name in dir 0 with next name in dir 1. 253 At the end of a dir, 254 pretend the "next name" in that dir is very large. */ 255 int nameorder = (!*names[0] ? 1 : !*names[1] ? -1 256 : compare_names (*names[0], *names[1])); 257 258 /* Prefer a file_name_cmp match if available. This algorithm is 259 O(N**2), where N is the number of names in a directory 260 that compare_names says are all equal, but in practice N 261 is so small it's not worth tuning. */ 262 if (nameorder == 0) 263 { 264 int raw_order = file_name_cmp (*names[0], *names[1]); 265 if (raw_order != 0) 266 { 267 int greater_side = raw_order < 0; 268 int lesser_side = 1 - greater_side; 269 char const **lesser = names[lesser_side]; 270 char const *greater_name = *names[greater_side]; 271 char const **p; 272 273 for (p = lesser + 1; 274 *p && compare_names (*p, greater_name) == 0; 275 p++) 276 { 277 int c = file_name_cmp (*p, greater_name); 278 if (0 <= c) 279 { 280 if (c == 0) 281 { 282 memmove (lesser + 1, lesser, 283 (char *) p - (char *) lesser); 284 *lesser = greater_name; 285 } 286 break; 287 } 288 } 289 } 290 } 291 292 int v1 = (*handle_file) (cmp, 293 0 < nameorder ? 0 : *names[0]++, 294 nameorder < 0 ? 0 : *names[1]++); 295 if (val < v1) 296 val = v1; 297 } 298 } 299 300 for (i = 0; i < 2; i++) 301 { 302 free (dirdata[i].names); 303 free (dirdata[i].data); 304 } 305 306 return val; 307 } 308 309 /* Return nonzero if CMP is looping recursively in argument I. */ 310 311 static bool 312 dir_loop (struct comparison const *cmp, int i) 313 { 314 struct comparison const *p = cmp; 315 while ((p = p->parent)) 316 if (0 < same_file (&p->file[i].stat, &cmp->file[i].stat)) 317 return true; 318 return false; 319 } 320 321 /* Find a matching filename in a directory. */ 322 323 char * 324 find_dir_file_pathname (char const *dir, char const *file) 325 { 326 char *val; 327 char const *match = file; 328 struct dirdata dirdata; 329 dirdata.names = NULL; 330 dirdata.data = NULL; 331 332 if (ignore_file_name_case) 333 { 334 struct file_data filedata; 335 filedata.name = dir; 336 filedata.desc = 0; 337 338 if (dir_read (&filedata, &dirdata)) 339 { 340 locale_specific_sorting = true; 341 if (setjmp (failed_locale_specific_sorting)) 342 match = file; /* longjmp may mess up MATCH. */ 343 else 344 { 345 for (char const **p = dirdata.names; *p; p++) 346 if (compare_names (*p, file) == 0) 347 { 348 if (file_name_cmp (*p, file) == 0) 349 { 350 match = *p; 351 break; 352 } 353 if (match == file) 354 match = *p; 355 } 356 } 357 } 358 } 359 360 val = file_name_concat (dir, match, NULL); 361 free (dirdata.names); 362 free (dirdata.data); 363 return val; 364 } 365