1 | /* Read, sort and compare two directories. Used for GNU DIFF. |
---|
2 | Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc. |
---|
3 | |
---|
4 | This file is part of GNU DIFF. |
---|
5 | |
---|
6 | GNU DIFF is free software; you can redistribute it and/or modify |
---|
7 | it under the terms of the GNU General Public License as published by |
---|
8 | the Free Software Foundation; either version 2, or (at your option) |
---|
9 | any later version. |
---|
10 | |
---|
11 | GNU DIFF is distributed in the hope that it will be useful, |
---|
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | GNU General Public License for more details. |
---|
15 | |
---|
16 | You should have received a copy of the GNU General Public License |
---|
17 | along with GNU DIFF; see the file COPYING. If not, write to |
---|
18 | the 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 | |
---|
27 | struct dirdata |
---|
28 | { |
---|
29 | char const **names; /* Sorted names of files in dir, 0-terminated. */ |
---|
30 | char *data; /* Allocated storage for file names. */ |
---|
31 | }; |
---|
32 | |
---|
33 | static int compare_names PARAMS((void const *, void const *)); |
---|
34 | static int dir_sort PARAMS((struct file_data const *, struct dirdata *)); |
---|
35 | |
---|
36 | static int |
---|
37 | dir_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 | |
---|
127 | static int |
---|
128 | compare_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 | |
---|
155 | int |
---|
156 | diff_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 | } |
---|