386BSD 0.1 development
[unix-history] / usr / src / usr.bin / diff / dir.c
CommitLineData
3c369cdd
WJ
1/* Read, sort and compare two directories. Used for GNU DIFF.
2 Copyright (C) 1988, 1989 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 1, 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
22static 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
28struct dirdata
29{
30 int length; /* # elements in `files' */
31 char **files; /* Sorted names of files in the dir */
32};
33
34static struct dirdata
35dir_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
109static int
110compare_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
135int
136diff_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}