-
+ 195F6A1A811E7D09915A8596772D8E0DDA1C9B2F7C7F1FCF32F9671FADE50794BDEACBCA12874C7A8B8A909D84BC69286B27832EA83B6E208E50D8B958C685BA
vtools/src/dir.c
(0 . 0)(1 . 218)
4142
4143 #include "diff.h"
4144 #include <error.h>
4145 #include <filenamecat.h>
4146 #include <setjmp.h>
4147 #include <xalloc.h>
4148 #include <dirent.h>
4149 #include <stdlib.h>
4150
4151 /* Read the directory named by |dir| and store into |dirdata| a sorted
4152 vector of filenames for its contents. |dir->desc == -1| means this
4153 directory is known to be nonexistent, so set |dirdata| to an empty
4154 vector. Return -1 (setting |errno|) if error, 0 otherwise. */
4155
4156 struct dirdata {
4157 size_t nnames; /* Number of names. */
4158 char const **names; /* Sorted names of files in dir, followed by 0. */
4159 char *data; /* Allocated storage for file names. */
4160 };
4161
4162 static bool dir_loop(struct comparison const *, int);
4163
4164
4165 /* Read a directory and get its vector of names. */
4166
4167 static bool
4168 dir_read(struct file_data const *dir, struct dirdata *dirdata) {
4169 register struct dirent *next;
4170 register size_t i;
4171
4172 /* Address of block containing the files that are described. */
4173 char const **names;
4174
4175 /* Number of files in directory. */
4176 size_t nnames;
4177
4178 /* Allocated and used storage for file name data. */
4179 char *data;
4180 size_t data_alloc, data_used;
4181
4182 dirdata->names = 0;
4183 dirdata->data = 0;
4184 nnames = 0;
4185 data = 0;
4186
4187 if (dir->desc != -1) {
4188 /* Open the directory and check for errors. */
4189 register DIR *reading = opendir(dir->name);
4190 if (!reading)
4191 return false;
4192
4193 /* Initialize the table of filenames. */
4194
4195 data_alloc = 512;
4196 data_used = 0;
4197 dirdata->data = data = xmalloc(data_alloc);
4198
4199 /* Read the directory entries, and insert the subfiles
4200 into the |data| table. */
4201
4202 while ((errno = 0, (next = readdir(reading)) != 0)) {
4203 char *d_name = next->d_name;
4204 size_t d_size = strlen (next->d_name) + 1;
4205
4206 /* Ignore "." and "..". */
4207 if (d_name[0] == '.'
4208 && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0)))
4209 continue;
4210
4211 while (data_alloc < data_used + d_size) {
4212 if (PTRDIFF_MAX / 2 <= data_alloc)
4213 xalloc_die();
4214 dirdata->data = data = xrealloc(data, data_alloc *= 2);
4215 }
4216
4217 memcpy (data + data_used, d_name, d_size);
4218 data_used += d_size;
4219 nnames++;
4220 }
4221 if (errno) {
4222 int e = errno;
4223 closedir(reading);
4224 errno = e;
4225 return false;
4226 }
4227 if (closedir(reading) != 0)
4228 return false;
4229 }
4230
4231 /* Create the |names| table from the |data| table. */
4232 if (PTRDIFF_MAX / sizeof *names - 1 <= nnames)
4233 xalloc_die();
4234 dirdata->names = names = xmalloc((nnames + 1) * sizeof *names);
4235 dirdata->nnames = nnames;
4236 for (i = 0; i < nnames; i++) {
4237 names[i] = data;
4238 data += strlen(data) + 1;
4239 }
4240 names[nnames] = 0;
4241 return true;
4242 }
4243
4244 /* Compare file names, returning a value compatible with strcmp. */
4245
4246 #define compare_names strcmp
4247
4248 /* Compare names FILE1 and FILE2 when sorting a directory. */
4249
4250 static int
4251 compare_names_for_qsort(void const *file1, void const *file2) {
4252 char const *const *f1 = file1;
4253 char const *const *f2 = file2;
4254 char const *name1 = *f1;
4255 char const *name2 = *f2;
4256 return compare_names(name1, name2);
4257 }
4258
4259 /* Compare the contents of two directories named in |cmp|. This is a
4260 top-level routine; it does everything necessary for diff on two
4261 directories.
4262
4263 |cmp->file[0].desc == -1| says directory |cmp->file[0]| doesn't
4264 exist, but pretend it is empty. Likewise for |cmp->file[1]|.
4265
4266 |handle_file| is a caller-provided subroutine called to handle each file.
4267 It gets three operands: |cmp|, name of file in dir 0, name of file in dir 1.
4268 These names are relative to the original working directory.
4269
4270 For a file that appears in only one of the dirs, one of the name-args
4271 to |handle_file| is zero.
4272
4273 Returns the maximum of all the values returned by |handle_file|,
4274 or |exit_trouble| if trouble is encountered in opening files. */
4275
4276 int
4277 diff_dirs(struct comparison const *cmp,
4278 int (*handle_file)(struct comparison const *,
4279 char const *, char const *)) {
4280 struct dirdata dirdata[2];
4281 int volatile val = EXIT_SUCCESS;
4282 int i;
4283
4284 if ((cmp->file[0].desc == -1 || dir_loop(cmp, 0))
4285 && (cmp->file[1].desc == -1 || dir_loop(cmp, 1))) {
4286 error(0, 0, "%s: recursive directory loop",
4287 cmp->file[cmp->file[0].desc == -1].name);
4288 return EXIT_TROUBLE;
4289 }
4290
4291 /* Get contents of both dirs. */
4292 for (i = 0; i < 2; i++)
4293 if (!dir_read(&cmp->file[i], &dirdata[i])) {
4294 perror_with_name(cmp->file[i].name);
4295 val = EXIT_TROUBLE;
4296 }
4297
4298 if (val == EXIT_SUCCESS) {
4299 char const **volatile names[2];
4300 names[0] = dirdata[0].names;
4301 names[1] = dirdata[1].names;
4302
4303 /* Sort the directories. */
4304 for (i = 0; i < 2; i++)
4305 qsort(names[i], dirdata[i].nnames, sizeof *dirdata[i].names,
4306 compare_names_for_qsort);
4307
4308 /* If |-S name| was given, and this is the topmost level of
4309 comparison, ignore all file names less than the specified
4310 starting name. */
4311
4312 if (starting_file && !cmp->parent) {
4313 while (*names[0] && compare_names(*names[0], starting_file) < 0)
4314 names[0]++;
4315 while (*names[1] && compare_names(*names[1], starting_file) < 0)
4316 names[1]++;
4317 }
4318
4319 /* Loop while files remain in one or both dirs. */
4320 while (*names[0] || *names[1]) {
4321 /* Compare next name in dir 0 with next name in dir 1. At
4322 the end of a dir, pretend the "next name" in that dir
4323 is very large. */
4324 int nameorder = (!*names[0] ? 1 : !*names[1] ? -1
4325 : compare_names(*names[0], *names[1]));
4326 int v1 = (*handle_file)(cmp,
4327 0 < nameorder ? 0 : *names[0]++,
4328 nameorder < 0 ? 0 : *names[1]++);
4329 if (val < v1)
4330 val = v1;
4331 }
4332 }
4333
4334 for (i = 0; i < 2; i++) {
4335 free(dirdata[i].names);
4336 free(dirdata[i].data);
4337 }
4338
4339 return val;
4340 }
4341
4342 /* Return nonzero if $cmp$ is looping recursively in argument $i$. */
4343
4344 static bool
4345 dir_loop(struct comparison const *cmp, int i) {
4346 struct comparison const *p = cmp;
4347 while ((p = p->parent))
4348 if (0 < same_file (&p->file[i].stat, &cmp->file[i].stat))
4349 return true;
4350 return false;
4351 }
4352
4353 /* Find a matching filename in a directory. */
4354
4355 char *
4356 find_dir_file_pathname(char const *dir, char const *file) {
4357 const char *match = file;
4358 return file_name_concat(dir, match, NULL);
4359 }