Commit | Line | Data |
---|---|---|
3c369cdd WJ |
1 | /* Read, sort and compare two directories. Used for GNU DIFF. |
2 | Copyright (C) 1988, 1989 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 1, 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 | static int compare_names (); | |
23 | ||
24 | /* Read the directory named DIRNAME and return a sorted vector | |
25 | of filenames for its contents. NONEX nonzero means this directory is | |
26 | known to be nonexistent, so return zero files. */ | |
27 | ||
28 | struct dirdata | |
29 | { | |
30 | int length; /* # elements in `files' */ | |
31 | char **files; /* Sorted names of files in the dir */ | |
32 | }; | |
33 | ||
34 | static struct dirdata | |
35 | dir_sort (dirname, nonex) | |
36 | char *dirname; | |
37 | int nonex; | |
38 | { | |
39 | register DIR *reading; | |
40 | register struct direct *next; | |
41 | struct dirdata dirdata; | |
42 | ||
43 | /* Address of block containing the files that are described. */ | |
44 | char **files; | |
45 | ||
46 | /* Length of block that `files' points to, measured in files. */ | |
47 | int nfiles; | |
48 | ||
49 | /* Index of first unused in `files'. */ | |
50 | int files_index; | |
51 | ||
52 | if (nonex) | |
53 | { | |
54 | dirdata.length = 0; | |
55 | dirdata.files = 0; | |
56 | return dirdata; | |
57 | } | |
58 | ||
59 | /* Open the directory and check for errors. */ | |
60 | reading = opendir (dirname); | |
61 | if (!reading) | |
62 | { | |
63 | perror_with_name (dirname); | |
64 | dirdata.length = -1; | |
65 | return dirdata; | |
66 | } | |
67 | ||
68 | /* Initialize the table of filenames. */ | |
69 | ||
70 | nfiles = 100; | |
71 | files = (char **) xmalloc (nfiles * sizeof (char *)); | |
72 | files_index = 0; | |
73 | ||
74 | /* Read the directory entries, and insert the subfiles | |
75 | into the `files' table. */ | |
76 | ||
77 | while (next = readdir (reading)) | |
78 | { | |
79 | /* Ignore the files `.' and `..' */ | |
80 | if (next->d_name[0] == '.' | |
81 | && (next->d_name[1] == 0 | |
82 | || (next->d_name[1] == '.' | |
83 | && next->d_name[2] == 0))) | |
84 | continue; | |
85 | ||
86 | if (files_index == nfiles) | |
87 | { | |
88 | nfiles *= 2; | |
89 | files | |
90 | = (char **) xrealloc (files, sizeof (char *) * nfiles); | |
91 | } | |
92 | files[files_index++] = concat (next->d_name, "", ""); | |
93 | } | |
94 | ||
95 | closedir (reading); | |
96 | ||
97 | /* Sort the table. */ | |
98 | qsort (files, files_index, sizeof (char *), compare_names); | |
99 | ||
100 | /* Return a description of location and length of the table. */ | |
101 | dirdata.files = files; | |
102 | dirdata.length = files_index; | |
103 | ||
104 | return dirdata; | |
105 | } | |
106 | ||
107 | /* Sort the files now in the table. */ | |
108 | ||
109 | static int | |
110 | compare_names (file1, file2) | |
111 | char **file1, **file2; | |
112 | { | |
113 | return strcmp (*file1, *file2); | |
114 | } | |
115 | \f | |
116 | /* Compare the contents of two directories named NAME1 and NAME2. | |
117 | This is a top-level routine; it does everything necessary for diff | |
118 | on two directories. | |
119 | ||
120 | NONEX1 nonzero says directory NAME1 doesn't exist, but pretend it is | |
121 | empty. Likewise NONEX2. | |
122 | ||
123 | HANDLE_FILE is a caller-provided subroutine called to handle each file. | |
124 | It gets five operands: dir and name (rel to original working dir) of file | |
125 | in dir 1, dir and name pathname of file in dir 2, and the recursion depth. | |
126 | ||
127 | For a file that appears in only one of the dirs, one of the name-args | |
128 | to HANDLE_FILE is zero. | |
129 | ||
130 | DEPTH is the current depth in recursion. | |
131 | ||
132 | Returns the maximum of all the values returned by HANDLE_FILE, | |
133 | or 2 if trouble is encountered in opening files. */ | |
134 | ||
135 | int | |
136 | diff_dirs (name1, name2, handle_file, depth, nonex1, nonex2) | |
137 | char *name1, *name2; | |
138 | int (*handle_file) (); | |
139 | int nonex1, nonex2; | |
140 | { | |
141 | struct dirdata data1, data2; | |
142 | register int i1, i2; | |
143 | int val = 0; | |
144 | int v1; | |
145 | ||
146 | /* Get sorted contents of both dirs. */ | |
147 | data1 = dir_sort (name1, nonex1); | |
148 | data2 = dir_sort (name2, nonex2); | |
149 | if (data1.length == -1 || data2.length == -1) | |
150 | { | |
151 | if (data1.length >= 0) | |
152 | free (data1.files); | |
153 | if (data2.length >= 0) | |
154 | free (data2.files); | |
155 | return 2; | |
156 | } | |
157 | ||
158 | i1 = 0; | |
159 | i2 = 0; | |
160 | ||
161 | /* If -Sname was specified, and this is the topmost level of comparison, | |
162 | ignore all file names less than the specified starting name. */ | |
163 | ||
164 | if (dir_start_file && depth == 0) | |
165 | { | |
166 | while (i1 < data1.length && strcmp (data1.files[i1], dir_start_file) < 0) | |
167 | i1++; | |
168 | while (i2 < data2.length && strcmp (data2.files[i2], dir_start_file) < 0) | |
169 | i2++; | |
170 | } | |
171 | ||
172 | /* Loop while files remain in one or both dirs. */ | |
173 | while (i1 < data1.length || i2 < data2.length) | |
174 | { | |
175 | int nameorder; | |
176 | ||
177 | /* Compare next name in dir 1 with next name in dir 2. | |
178 | At the end of a dir, | |
179 | pretend the "next name" in that dir is very large. */ | |
180 | ||
181 | if (i1 == data1.length) | |
182 | nameorder = 1; | |
183 | else if (i2 == data2.length) | |
184 | nameorder = -1; | |
185 | else | |
186 | nameorder = strcmp (data1.files[i1], data2.files[i2]); | |
187 | ||
188 | if (nameorder == 0) | |
189 | { | |
190 | /* We have found a file (or subdir) in common between both dirs. | |
191 | Compare the two files. */ | |
192 | v1 = (*handle_file) (name1, data1.files[i1], name2, data2.files[i2], | |
193 | depth + 1); | |
194 | i1++, i2++; | |
195 | } | |
196 | if (nameorder < 0) | |
197 | { | |
198 | /* Next filename in dir 1 is less; that is a file in dir 1 only. */ | |
199 | v1 = (*handle_file) (name1, data1.files[i1], name2, 0, depth + 1); | |
200 | i1++; | |
201 | } | |
202 | if (nameorder > 0) | |
203 | { | |
204 | /* Next filename in dir 2 is less; that is a file in dir 2 only. */ | |
205 | v1 = (*handle_file) (name1, 0, name2, data2.files[i2], depth + 1); | |
206 | i2++; | |
207 | } | |
208 | if (v1 > val) | |
209 | val = v1; | |
210 | } | |
211 | if (data1.files) | |
212 | free (data1.files); | |
213 | if (data2.files) | |
214 | free (data2.files); | |
215 | ||
216 | return val; | |
217 | } |