xref: /netbsd-src/external/gpl2/diffutils/dist/src/dir.c (revision 75f6d617e282811cb173c2ccfbf5df0dd71f7045)
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