1128456Sdes/* Read, sort and compare two directories. Used for GNU DIFF. 2124208Sdes 398937Sdes Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002, 4126274Sdes 2004 Free Software Foundation, Inc. 598937Sdes 6126274Sdes This file is part of GNU DIFF. 7126274Sdes 8126274Sdes GNU DIFF is free software; you can redistribute it and/or modify 998937Sdes it under the terms of the GNU General Public License as published by 10126274Sdes the Free Software Foundation; either version 2, or (at your option) 11126274Sdes any later version. 12126274Sdes 13126274Sdes GNU DIFF is distributed in the hope that it will be useful, 14126274Sdes but WITHOUT ANY WARRANTY; without even the implied warranty of 15126274Sdes MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16126274Sdes GNU General Public License for more details. 1798937Sdes 1898937Sdes You should have received a copy of the GNU General Public License 1998937Sdes along with this program; see the file COPYING. 2098937Sdes If not, write to the Free Software Foundation, 2198937Sdes 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 22124208Sdes 2398937Sdes#include "diff.h" 24124208Sdes#include <error.h> 2598937Sdes#include <exclude.h> 2698937Sdes#include <setjmp.h> 2798937Sdes#include <strcase.h> 2898937Sdes#include <xalloc.h> 2998937Sdes 3098937Sdes/* Read the directory named by DIR and store into DIRDATA a sorted vector 31124208Sdes of filenames for its contents. DIR->desc == -1 means this directory is 3298937Sdes known to be nonexistent, so set DIRDATA to an empty vector. 3398937Sdes Return -1 (setting errno) if error, 0 otherwise. */ 3498937Sdes 35124208Sdesstruct dirdata 3698937Sdes{ 3798937Sdes size_t nnames; /* Number of names. */ 3898937Sdes char const **names; /* Sorted names of files in dir, followed by 0. */ 39124208Sdes char *data; /* Allocated storage for file names. */ 4098937Sdes}; 4198937Sdes 4298937Sdes/* Whether file names in directories should be compared with 43124208Sdes locale-specific sorting. */ 4498937Sdesstatic bool locale_specific_sorting; 4598937Sdes 4698937Sdes/* Where to go if locale-specific sorting fails. */ 47124208Sdesstatic jmp_buf failed_locale_specific_sorting; 4898937Sdes 4998937Sdesstatic bool dir_loop (struct comparison const *, int); 5098937Sdesstatic int compare_names_for_qsort (void const *, void const *); 51124208Sdes 5298937Sdes 5398937Sdes/* Read a directory and get its vector of names. */ 5498937Sdes 5598937Sdesstatic bool 5698937Sdesdir_read (struct file_data const *dir, struct dirdata *dirdata) 5798937Sdes{ 5898937Sdes register struct dirent *next; 5998937Sdes register size_t i; 6098937Sdes 6198937Sdes /* Address of block containing the files that are described. */ 6298937Sdes char const **names; 63124208Sdes 6498937Sdes /* Number of files in directory. */ 6598937Sdes size_t nnames; 6698937Sdes 67124208Sdes /* Allocated and used storage for file name data. */ 6898937Sdes char *data; 6998937Sdes size_t data_alloc, data_used; 7098937Sdes 71124208Sdes dirdata->names = 0; 7298937Sdes dirdata->data = 0; 7398937Sdes nnames = 0; 74113908Sdes data = 0; 75113908Sdes 76113908Sdes if (dir->desc != -1) 77113908Sdes { 78113908Sdes /* Open the directory and check for errors. */ 79113908Sdes register DIR *reading = opendir (dir->name); 80113908Sdes if (!reading) 81124208Sdes return false; 82113908Sdes 8398937Sdes /* Initialize the table of filenames. */ 84124208Sdes 85124208Sdes data_alloc = 512; 86124208Sdes data_used = 0; 87124208Sdes dirdata->data = data = xmalloc (data_alloc); 88124208Sdes 89124208Sdes /* Read the directory entries, and insert the subfiles 90124208Sdes into the `data' table. */ 91124208Sdes 92128456Sdes while ((errno = 0, (next = readdir (reading)) != 0)) 93128456Sdes { 94128456Sdes char *d_name = next->d_name; 95128456Sdes size_t d_size = NAMLEN (next) + 1; 96124208Sdes 97124208Sdes /* Ignore "." and "..". */ 98124208Sdes if (d_name[0] == '.' 99124208Sdes && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0))) 100124208Sdes continue; 101124208Sdes 10298937Sdes if (excluded_filename (excluded, d_name)) 103 continue; 104 105 while (data_alloc < data_used + d_size) 106 { 107 if (PTRDIFF_MAX / 2 <= data_alloc) 108 xalloc_die (); 109 dirdata->data = data = xrealloc (data, data_alloc *= 2); 110 } 111 112 memcpy (data + data_used, d_name, d_size); 113 data_used += d_size; 114 nnames++; 115 } 116 if (errno) 117 { 118 int e = errno; 119 closedir (reading); 120 errno = e; 121 return false; 122 } 123#if CLOSEDIR_VOID 124 closedir (reading); 125#else 126 if (closedir (reading) != 0) 127 return false; 128#endif 129 } 130 131 /* Create the `names' table from the `data' table. */ 132 if (PTRDIFF_MAX / sizeof *names - 1 <= nnames) 133 xalloc_die (); 134 dirdata->names = names = xmalloc ((nnames + 1) * sizeof *names); 135 dirdata->nnames = nnames; 136 for (i = 0; i < nnames; i++) 137 { 138 names[i] = data; 139 data += strlen (data) + 1; 140 } 141 names[nnames] = 0; 142 return true; 143} 144 145/* Compare file names, returning a value compatible with strcmp. */ 146 147static int 148compare_names (char const *name1, char const *name2) 149{ 150 if (locale_specific_sorting) 151 { 152 int r; 153 errno = 0; 154 if (ignore_file_name_case) 155 r = strcasecoll (name1, name2); 156 else 157 r = strcoll (name1, name2); 158 if (errno) 159 { 160 error (0, errno, _("cannot compare file names `%s' and `%s'"), 161 name1, name2); 162 longjmp (failed_locale_specific_sorting, 1); 163 } 164 return r; 165 } 166 167 return (ignore_file_name_case 168 ? strcasecmp (name1, name2) 169 : file_name_cmp (name1, name2)); 170} 171 172/* A wrapper for compare_names suitable as an argument for qsort. */ 173 174static int 175compare_names_for_qsort (void const *file1, void const *file2) 176{ 177 char const *const *f1 = file1; 178 char const *const *f2 = file2; 179 return compare_names (*f1, *f2); 180} 181 182/* Compare the contents of two directories named in CMP. 183 This is a top-level routine; it does everything necessary for diff 184 on two directories. 185 186 CMP->file[0].desc == -1 says directory CMP->file[0] doesn't exist, 187 but pretend it is empty. Likewise for CMP->file[1]. 188 189 HANDLE_FILE is a caller-provided subroutine called to handle each file. 190 It gets three operands: CMP, name of file in dir 0, name of file in dir 1. 191 These names are relative to the original working directory. 192 193 For a file that appears in only one of the dirs, one of the name-args 194 to HANDLE_FILE is zero. 195 196 Returns the maximum of all the values returned by HANDLE_FILE, 197 or EXIT_TROUBLE if trouble is encountered in opening files. */ 198 199int 200diff_dirs (struct comparison const *cmp, 201 int (*handle_file) (struct comparison const *, 202 char const *, char const *)) 203{ 204 struct dirdata dirdata[2]; 205 int volatile val = EXIT_SUCCESS; 206 int i; 207 208 if ((cmp->file[0].desc == -1 || dir_loop (cmp, 0)) 209 && (cmp->file[1].desc == -1 || dir_loop (cmp, 1))) 210 { 211 error (0, 0, "%s: recursive directory loop", 212 cmp->file[cmp->file[0].desc == -1].name); 213 return EXIT_TROUBLE; 214 } 215 216 /* Get contents of both dirs. */ 217 for (i = 0; i < 2; i++) 218 if (! dir_read (&cmp->file[i], &dirdata[i])) 219 { 220 perror_with_name (cmp->file[i].name); 221 val = EXIT_TROUBLE; 222 } 223 224 if (val == EXIT_SUCCESS) 225 { 226 char const **volatile names[2]; 227 names[0] = dirdata[0].names; 228 names[1] = dirdata[1].names; 229 230 /* Use locale-specific sorting if possible, else native byte order. */ 231 locale_specific_sorting = true; 232 if (setjmp (failed_locale_specific_sorting)) 233 locale_specific_sorting = false; 234 235 /* Sort the directories. */ 236 for (i = 0; i < 2; i++) 237 qsort (names[i], dirdata[i].nnames, sizeof *dirdata[i].names, 238 compare_names_for_qsort); 239 240 /* If `-S name' was given, and this is the topmost level of comparison, 241 ignore all file names less than the specified starting name. */ 242 243 if (starting_file && ! cmp->parent) 244 { 245 while (*names[0] && compare_names (*names[0], starting_file) < 0) 246 names[0]++; 247 while (*names[1] && compare_names (*names[1], starting_file) < 0) 248 names[1]++; 249 } 250 251 /* Loop while files remain in one or both dirs. */ 252 while (*names[0] || *names[1]) 253 { 254 /* Compare next name in dir 0 with next name in dir 1. 255 At the end of a dir, 256 pretend the "next name" in that dir is very large. */ 257 int nameorder = (!*names[0] ? 1 : !*names[1] ? -1 258 : compare_names (*names[0], *names[1])); 259 int v1 = (*handle_file) (cmp, 260 0 < nameorder ? 0 : *names[0]++, 261 nameorder < 0 ? 0 : *names[1]++); 262 if (val < v1) 263 val = v1; 264 } 265 } 266 267 for (i = 0; i < 2; i++) 268 { 269 if (dirdata[i].names) 270 free (dirdata[i].names); 271 if (dirdata[i].data) 272 free (dirdata[i].data); 273 } 274 275 return val; 276} 277 278/* Return nonzero if CMP is looping recursively in argument I. */ 279 280static bool 281dir_loop (struct comparison const *cmp, int i) 282{ 283 struct comparison const *p = cmp; 284 while ((p = p->parent)) 285 if (0 < same_file (&p->file[i].stat, &cmp->file[i].stat)) 286 return true; 287 return false; 288} 289