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