1170754Sdelphij/* Read, sort and compare two directories. Used for GNU DIFF. 2170754Sdelphij 3170754Sdelphij Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002, 4170754Sdelphij 2004 Free Software Foundation, Inc. 5170754Sdelphij 6170754Sdelphij This file is part of GNU DIFF. 7170754Sdelphij 8170754Sdelphij GNU DIFF is free software; you can redistribute it and/or modify 9170754Sdelphij it under the terms of the GNU General Public License as published by 10170754Sdelphij the Free Software Foundation; either version 2, or (at your option) 11170754Sdelphij any later version. 12170754Sdelphij 13170754Sdelphij GNU DIFF is distributed in the hope that it will be useful, 14170754Sdelphij but WITHOUT ANY WARRANTY; without even the implied warranty of 15170754Sdelphij MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16170754Sdelphij GNU General Public License for more details. 17170754Sdelphij 18170754Sdelphij You should have received a copy of the GNU General Public License 19170754Sdelphij along with this program; see the file COPYING. 20170754Sdelphij If not, write to the Free Software Foundation, 21170754Sdelphij 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 22170754Sdelphij 23170754Sdelphij#include "diff.h" 24170754Sdelphij#include <error.h> 25170754Sdelphij#include <exclude.h> 26170754Sdelphij#include <setjmp.h> 27170754Sdelphij#include <strcase.h> 28170754Sdelphij#include <xalloc.h> 29170754Sdelphij 30170754Sdelphij/* Read the directory named by DIR and store into DIRDATA a sorted vector 31170754Sdelphij of filenames for its contents. DIR->desc == -1 means this directory is 32170754Sdelphij known to be nonexistent, so set DIRDATA to an empty vector. 33170754Sdelphij Return -1 (setting errno) if error, 0 otherwise. */ 34170754Sdelphij 35170754Sdelphijstruct dirdata 36170754Sdelphij{ 37170754Sdelphij size_t nnames; /* Number of names. */ 38170754Sdelphij char const **names; /* Sorted names of files in dir, followed by 0. */ 39170754Sdelphij char *data; /* Allocated storage for file names. */ 40170754Sdelphij}; 41170754Sdelphij 42170754Sdelphij/* Whether file names in directories should be compared with 43170754Sdelphij locale-specific sorting. */ 44170754Sdelphijstatic bool locale_specific_sorting; 45170754Sdelphij 46170754Sdelphij/* Where to go if locale-specific sorting fails. */ 47170754Sdelphijstatic jmp_buf failed_locale_specific_sorting; 48170754Sdelphij 49170754Sdelphijstatic bool dir_loop (struct comparison const *, int); 50170754Sdelphijstatic int compare_names_for_qsort (void const *, void const *); 51170754Sdelphij 52170754Sdelphij 53170754Sdelphij/* Read a directory and get its vector of names. */ 54170754Sdelphij 55170754Sdelphijstatic bool 56170754Sdelphijdir_read (struct file_data const *dir, struct dirdata *dirdata) 57170754Sdelphij{ 58170754Sdelphij register struct dirent *next; 59170754Sdelphij register size_t i; 60170754Sdelphij 61170754Sdelphij /* Address of block containing the files that are described. */ 62170754Sdelphij char const **names; 63170754Sdelphij 64170754Sdelphij /* Number of files in directory. */ 65170754Sdelphij size_t nnames; 66170754Sdelphij 67170754Sdelphij /* Allocated and used storage for file name data. */ 68170754Sdelphij char *data; 69170754Sdelphij size_t data_alloc, data_used; 70170754Sdelphij 71170754Sdelphij dirdata->names = 0; 72170754Sdelphij dirdata->data = 0; 73170754Sdelphij nnames = 0; 74170754Sdelphij data = 0; 75170754Sdelphij 76170754Sdelphij if (dir->desc != -1) 77170754Sdelphij { 78170754Sdelphij /* Open the directory and check for errors. */ 79170754Sdelphij register DIR *reading = opendir (dir->name); 80170754Sdelphij if (!reading) 81170754Sdelphij return false; 82170754Sdelphij 83170754Sdelphij /* Initialize the table of filenames. */ 84170754Sdelphij 85170754Sdelphij data_alloc = 512; 86170754Sdelphij data_used = 0; 87170754Sdelphij dirdata->data = data = xmalloc (data_alloc); 88170754Sdelphij 89170754Sdelphij /* Read the directory entries, and insert the subfiles 90170754Sdelphij into the `data' table. */ 91170754Sdelphij 92170754Sdelphij while ((errno = 0, (next = readdir (reading)) != 0)) 93170754Sdelphij { 94170754Sdelphij char *d_name = next->d_name; 95170754Sdelphij size_t d_size = NAMLEN (next) + 1; 96170754Sdelphij 97170754Sdelphij /* Ignore "." and "..". */ 98170754Sdelphij if (d_name[0] == '.' 99170754Sdelphij && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0))) 100170754Sdelphij continue; 101170754Sdelphij 102170754Sdelphij if (excluded_filename (excluded, d_name)) 103170754Sdelphij continue; 104170754Sdelphij 105170754Sdelphij while (data_alloc < data_used + d_size) 106170754Sdelphij { 107170754Sdelphij if (PTRDIFF_MAX / 2 <= data_alloc) 108170754Sdelphij xalloc_die (); 109170754Sdelphij dirdata->data = data = xrealloc (data, data_alloc *= 2); 110170754Sdelphij } 111170754Sdelphij 112170754Sdelphij memcpy (data + data_used, d_name, d_size); 113170754Sdelphij data_used += d_size; 114170754Sdelphij nnames++; 115170754Sdelphij } 116170754Sdelphij if (errno) 117170754Sdelphij { 118170754Sdelphij int e = errno; 119170754Sdelphij closedir (reading); 120170754Sdelphij errno = e; 121170754Sdelphij return false; 122170754Sdelphij } 123170754Sdelphij#if CLOSEDIR_VOID 124170754Sdelphij closedir (reading); 125170754Sdelphij#else 126170754Sdelphij if (closedir (reading) != 0) 127170754Sdelphij return false; 128170754Sdelphij#endif 129170754Sdelphij } 130170754Sdelphij 131170754Sdelphij /* Create the `names' table from the `data' table. */ 132170754Sdelphij if (PTRDIFF_MAX / sizeof *names - 1 <= nnames) 133170754Sdelphij xalloc_die (); 134170754Sdelphij dirdata->names = names = xmalloc ((nnames + 1) * sizeof *names); 135170754Sdelphij dirdata->nnames = nnames; 136170754Sdelphij for (i = 0; i < nnames; i++) 137170754Sdelphij { 138170754Sdelphij names[i] = data; 139170754Sdelphij data += strlen (data) + 1; 140170754Sdelphij } 141170754Sdelphij names[nnames] = 0; 142170754Sdelphij return true; 143170754Sdelphij} 144170754Sdelphij 145170754Sdelphij/* Compare file names, returning a value compatible with strcmp. */ 146170754Sdelphij 147170754Sdelphijstatic int 148170754Sdelphijcompare_names (char const *name1, char const *name2) 149170754Sdelphij{ 150170754Sdelphij if (locale_specific_sorting) 151170754Sdelphij { 152170754Sdelphij int r; 153170754Sdelphij errno = 0; 154170754Sdelphij if (ignore_file_name_case) 155170754Sdelphij r = strcasecoll (name1, name2); 156170754Sdelphij else 157170754Sdelphij r = strcoll (name1, name2); 158170754Sdelphij if (errno) 159170754Sdelphij { 160170754Sdelphij error (0, errno, _("cannot compare file names `%s' and `%s'"), 161170754Sdelphij name1, name2); 162170754Sdelphij longjmp (failed_locale_specific_sorting, 1); 163170754Sdelphij } 164170754Sdelphij return r; 165170754Sdelphij } 166170754Sdelphij 167170754Sdelphij return (ignore_file_name_case 168170754Sdelphij ? strcasecmp (name1, name2) 169170754Sdelphij : file_name_cmp (name1, name2)); 170170754Sdelphij} 171170754Sdelphij 172170754Sdelphij/* A wrapper for compare_names suitable as an argument for qsort. */ 173170754Sdelphij 174170754Sdelphijstatic int 175170754Sdelphijcompare_names_for_qsort (void const *file1, void const *file2) 176170754Sdelphij{ 177170754Sdelphij char const *const *f1 = file1; 178170754Sdelphij char const *const *f2 = file2; 179170754Sdelphij return compare_names (*f1, *f2); 180170754Sdelphij} 181170754Sdelphij 182170754Sdelphij/* Compare the contents of two directories named in CMP. 183170754Sdelphij This is a top-level routine; it does everything necessary for diff 184170754Sdelphij on two directories. 185170754Sdelphij 186170754Sdelphij CMP->file[0].desc == -1 says directory CMP->file[0] doesn't exist, 187170754Sdelphij but pretend it is empty. Likewise for CMP->file[1]. 188170754Sdelphij 189170754Sdelphij HANDLE_FILE is a caller-provided subroutine called to handle each file. 190170754Sdelphij It gets three operands: CMP, name of file in dir 0, name of file in dir 1. 191170754Sdelphij These names are relative to the original working directory. 192170754Sdelphij 193170754Sdelphij For a file that appears in only one of the dirs, one of the name-args 194170754Sdelphij to HANDLE_FILE is zero. 195170754Sdelphij 196170754Sdelphij Returns the maximum of all the values returned by HANDLE_FILE, 197170754Sdelphij or EXIT_TROUBLE if trouble is encountered in opening files. */ 198170754Sdelphij 199170754Sdelphijint 200170754Sdelphijdiff_dirs (struct comparison const *cmp, 201170754Sdelphij int (*handle_file) (struct comparison const *, 202170754Sdelphij char const *, char const *)) 203170754Sdelphij{ 204170754Sdelphij struct dirdata dirdata[2]; 205170754Sdelphij int volatile val = EXIT_SUCCESS; 206170754Sdelphij int i; 207170754Sdelphij 208170754Sdelphij if ((cmp->file[0].desc == -1 || dir_loop (cmp, 0)) 209170754Sdelphij && (cmp->file[1].desc == -1 || dir_loop (cmp, 1))) 210170754Sdelphij { 211170754Sdelphij error (0, 0, "%s: recursive directory loop", 212170754Sdelphij cmp->file[cmp->file[0].desc == -1].name); 213170754Sdelphij return EXIT_TROUBLE; 214170754Sdelphij } 215170754Sdelphij 216170754Sdelphij /* Get contents of both dirs. */ 217170754Sdelphij for (i = 0; i < 2; i++) 218170754Sdelphij if (! dir_read (&cmp->file[i], &dirdata[i])) 219170754Sdelphij { 220170754Sdelphij perror_with_name (cmp->file[i].name); 221170754Sdelphij val = EXIT_TROUBLE; 222170754Sdelphij } 223170754Sdelphij 224170754Sdelphij if (val == EXIT_SUCCESS) 225170754Sdelphij { 226170754Sdelphij char const **volatile names[2]; 227170754Sdelphij names[0] = dirdata[0].names; 228170754Sdelphij names[1] = dirdata[1].names; 229170754Sdelphij 230170754Sdelphij /* Use locale-specific sorting if possible, else native byte order. */ 231170754Sdelphij locale_specific_sorting = true; 232170754Sdelphij if (setjmp (failed_locale_specific_sorting)) 233170754Sdelphij locale_specific_sorting = false; 234170754Sdelphij 235170754Sdelphij /* Sort the directories. */ 236170754Sdelphij for (i = 0; i < 2; i++) 237170754Sdelphij qsort (names[i], dirdata[i].nnames, sizeof *dirdata[i].names, 238170754Sdelphij compare_names_for_qsort); 239170754Sdelphij 240170754Sdelphij /* If `-S name' was given, and this is the topmost level of comparison, 241170754Sdelphij ignore all file names less than the specified starting name. */ 242170754Sdelphij 243170754Sdelphij if (starting_file && ! cmp->parent) 244170754Sdelphij { 245170754Sdelphij while (*names[0] && compare_names (*names[0], starting_file) < 0) 246170754Sdelphij names[0]++; 247170754Sdelphij while (*names[1] && compare_names (*names[1], starting_file) < 0) 248170754Sdelphij names[1]++; 249170754Sdelphij } 250170754Sdelphij 251170754Sdelphij /* Loop while files remain in one or both dirs. */ 252170754Sdelphij while (*names[0] || *names[1]) 253170754Sdelphij { 254170754Sdelphij /* Compare next name in dir 0 with next name in dir 1. 255170754Sdelphij At the end of a dir, 256170754Sdelphij pretend the "next name" in that dir is very large. */ 257170754Sdelphij int nameorder = (!*names[0] ? 1 : !*names[1] ? -1 258170754Sdelphij : compare_names (*names[0], *names[1])); 259170754Sdelphij int v1 = (*handle_file) (cmp, 260170754Sdelphij 0 < nameorder ? 0 : *names[0]++, 261170754Sdelphij nameorder < 0 ? 0 : *names[1]++); 262170754Sdelphij if (val < v1) 263170754Sdelphij val = v1; 264170754Sdelphij } 265170754Sdelphij } 266170754Sdelphij 267170754Sdelphij for (i = 0; i < 2; i++) 268170754Sdelphij { 269170754Sdelphij if (dirdata[i].names) 270170754Sdelphij free (dirdata[i].names); 271170754Sdelphij if (dirdata[i].data) 272170754Sdelphij free (dirdata[i].data); 273170754Sdelphij } 274170754Sdelphij 275170754Sdelphij return val; 276170754Sdelphij} 277170754Sdelphij 278170754Sdelphij/* Return nonzero if CMP is looping recursively in argument I. */ 279170754Sdelphij 280170754Sdelphijstatic bool 281170754Sdelphijdir_loop (struct comparison const *cmp, int i) 282170754Sdelphij{ 283170754Sdelphij struct comparison const *p = cmp; 284170754Sdelphij while ((p = p->parent)) 285170754Sdelphij if (0 < same_file (&p->file[i].stat, &cmp->file[i].stat)) 286170754Sdelphij return true; 287170754Sdelphij return false; 288170754Sdelphij} 289