source: trunk/third/diffutils/dir.c @ 16149

Revision 16149, 6.0 KB checked in by rbasch, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16148, which included commits to RCS files with non-trunk default branches.
Line 
1/* Read, sort and compare two directories.  Used for GNU DIFF.
2   Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
3
4This file is part of GNU DIFF.
5
6GNU DIFF is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU DIFF is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU DIFF; see the file COPYING.  If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20#include "diff.h"
21
22/* Read the directory named by DIR and store into DIRDATA a sorted vector
23   of filenames for its contents.  DIR->desc == -1 means this directory is
24   known to be nonexistent, so set DIRDATA to an empty vector.
25   Return -1 (setting errno) if error, 0 otherwise.  */
26
27struct dirdata
28{
29  char const **names;   /* Sorted names of files in dir, 0-terminated.  */
30  char *data;   /* Allocated storage for file names.  */
31};
32
33static int compare_names PARAMS((void const *, void const *));
34static int dir_sort PARAMS((struct file_data const *, struct dirdata *));
35
36static int
37dir_sort (dir, dirdata)
38     struct file_data const *dir;
39     struct dirdata *dirdata;
40{
41  register struct dirent *next;
42  register int i;
43
44  /* Address of block containing the files that are described.  */
45  char const **names;
46
47  /* Number of files in directory.  */
48  size_t nnames;
49
50  /* Allocated and used storage for file name data.  */
51  char *data;
52  size_t data_alloc, data_used;
53
54  dirdata->names = 0;
55  dirdata->data = 0;
56  nnames = 0;
57  data = 0;
58
59  if (dir->desc != -1)
60    {
61      /* Open the directory and check for errors.  */
62      register DIR *reading = opendir (dir->name);
63      if (!reading)
64        return -1;
65
66      /* Initialize the table of filenames.  */
67
68      data_alloc = max (1, (size_t) dir->stat.st_size);
69      data_used = 0;
70      dirdata->data = data = xmalloc (data_alloc);
71
72      /* Read the directory entries, and insert the subfiles
73         into the `data' table.  */
74
75      while ((errno = 0, (next = readdir (reading)) != 0))
76        {
77          char *d_name = next->d_name;
78          size_t d_size = NAMLEN (next) + 1;
79
80          /* Ignore the files `.' and `..' */
81          if (d_name[0] == '.'
82              && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0)))
83            continue;
84
85          if (excluded_filename (d_name))
86            continue;
87
88          while (data_alloc < data_used + d_size)
89            dirdata->data = data = xrealloc (data, data_alloc *= 2);
90          memcpy (data + data_used, d_name, d_size);
91          data_used += d_size;
92          nnames++;
93        }
94      if (errno)
95        {
96          int e = errno;
97          closedir (reading);
98          errno = e;
99          return -1;
100        }
101#if CLOSEDIR_VOID
102      closedir (reading);
103#else
104      if (closedir (reading) != 0)
105        return -1;
106#endif
107    }
108
109  /* Create the `names' table from the `data' table.  */
110  dirdata->names = names = (char const **) xmalloc (sizeof (char *)
111                                                    * (nnames + 1));
112  for (i = 0;  i < nnames;  i++)
113    {
114      names[i] = data;
115      data += strlen (data) + 1;
116    }
117  names[nnames] = 0;
118
119  /* Sort the table.  */
120  qsort (names, nnames, sizeof (char *), compare_names);
121
122  return 0;
123}
124
125/* Sort the files now in the table.  */
126
127static int
128compare_names (file1, file2)
129     void const *file1, *file2;
130{
131  return filename_cmp (* (char const *const *) file1,
132                       * (char const *const *) file2);
133}
134
135/* Compare the contents of two directories named in FILEVEC[0] and FILEVEC[1].
136   This is a top-level routine; it does everything necessary for diff
137   on two directories.
138
139   FILEVEC[0].desc == -1 says directory FILEVEC[0] doesn't exist,
140   but pretend it is empty.  Likewise for FILEVEC[1].
141
142   HANDLE_FILE is a caller-provided subroutine called to handle each file.
143   It gets five operands: dir and name (rel to original working dir) of file
144   in dir 0, dir and name pathname of file in dir 1, and the recursion depth.
145
146   For a file that appears in only one of the dirs, one of the name-args
147   to HANDLE_FILE is zero.
148
149   DEPTH is the current depth in recursion, used for skipping top-level
150   files by the -S option.
151
152   Returns the maximum of all the values returned by HANDLE_FILE,
153   or 2 if trouble is encountered in opening files.  */
154
155int
156diff_dirs (filevec, handle_file, depth)
157     struct file_data const filevec[];
158     int (*handle_file) PARAMS((char const *, char const *, char const *, char const *, int));
159     int depth;
160{
161  struct dirdata dirdata[2];
162  int val = 0;                  /* Return value.  */
163  int i;
164
165  /* Get sorted contents of both dirs.  */
166  for (i = 0; i < 2; i++)
167    if (dir_sort (&filevec[i], &dirdata[i]) != 0)
168      {
169        perror_with_name (filevec[i].name);
170        val = 2;
171      }
172
173  if (val == 0)
174    {
175      register char const * const *names0 = dirdata[0].names;
176      register char const * const *names1 = dirdata[1].names;
177      char const *name0 = filevec[0].name;
178      char const *name1 = filevec[1].name;
179
180      /* If `-S name' was given, and this is the topmost level of comparison,
181         ignore all file names less than the specified starting name.  */
182
183      if (dir_start_file && depth == 0)
184        {
185          while (*names0 && filename_cmp (*names0, dir_start_file) < 0)
186            names0++;
187          while (*names1 && filename_cmp (*names1, dir_start_file) < 0)
188            names1++;
189        }
190
191      /* Loop while files remain in one or both dirs.  */
192      while (*names0 || *names1)
193        {
194          /* Compare next name in dir 0 with next name in dir 1.
195             At the end of a dir,
196             pretend the "next name" in that dir is very large.  */
197          int nameorder = (!*names0 ? 1 : !*names1 ? -1
198                           : filename_cmp (*names0, *names1));
199          int v1 = (*handle_file) (name0, 0 < nameorder ? 0 : *names0++,
200                                   name1, nameorder < 0 ? 0 : *names1++,
201                                   depth + 1);
202          if (v1 > val)
203            val = v1;
204        }
205    }
206
207  for (i = 0; i < 2; i++)
208    {
209      if (dirdata[i].names)
210        free (dirdata[i].names);
211      if (dirdata[i].data)
212        free (dirdata[i].data);
213    }
214
215  return val;
216}
Note: See TracBrowser for help on using the repository browser.