118fd37a7SXin LI /* Read, sort and compare two directories. Used for GNU DIFF.
218fd37a7SXin LI
318fd37a7SXin LI Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002,
418fd37a7SXin LI 2004 Free Software Foundation, Inc.
518fd37a7SXin LI
618fd37a7SXin LI This file is part of GNU DIFF.
718fd37a7SXin LI
818fd37a7SXin LI GNU DIFF is free software; you can redistribute it and/or modify
918fd37a7SXin LI it under the terms of the GNU General Public License as published by
1018fd37a7SXin LI the Free Software Foundation; either version 2, or (at your option)
1118fd37a7SXin LI any later version.
1218fd37a7SXin LI
1318fd37a7SXin LI GNU DIFF is distributed in the hope that it will be useful,
1418fd37a7SXin LI but WITHOUT ANY WARRANTY; without even the implied warranty of
1518fd37a7SXin LI MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1618fd37a7SXin LI GNU General Public License for more details.
1718fd37a7SXin LI
1818fd37a7SXin LI You should have received a copy of the GNU General Public License
1918fd37a7SXin LI along with this program; see the file COPYING.
2018fd37a7SXin LI If not, write to the Free Software Foundation,
2118fd37a7SXin LI 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
2218fd37a7SXin LI
2318fd37a7SXin LI #include "diff.h"
2418fd37a7SXin LI #include <error.h>
2518fd37a7SXin LI #include <exclude.h>
2618fd37a7SXin LI #include <setjmp.h>
2718fd37a7SXin LI #include <strcase.h>
2818fd37a7SXin LI #include <xalloc.h>
2918fd37a7SXin LI
3018fd37a7SXin LI /* Read the directory named by DIR and store into DIRDATA a sorted vector
3118fd37a7SXin LI of filenames for its contents. DIR->desc == -1 means this directory is
3218fd37a7SXin LI known to be nonexistent, so set DIRDATA to an empty vector.
3318fd37a7SXin LI Return -1 (setting errno) if error, 0 otherwise. */
3418fd37a7SXin LI
3518fd37a7SXin LI struct dirdata
3618fd37a7SXin LI {
3718fd37a7SXin LI size_t nnames; /* Number of names. */
3818fd37a7SXin LI char const **names; /* Sorted names of files in dir, followed by 0. */
3918fd37a7SXin LI char *data; /* Allocated storage for file names. */
4018fd37a7SXin LI };
4118fd37a7SXin LI
4218fd37a7SXin LI /* Whether file names in directories should be compared with
4318fd37a7SXin LI locale-specific sorting. */
4418fd37a7SXin LI static bool locale_specific_sorting;
4518fd37a7SXin LI
4618fd37a7SXin LI /* Where to go if locale-specific sorting fails. */
4718fd37a7SXin LI static jmp_buf failed_locale_specific_sorting;
4818fd37a7SXin LI
4918fd37a7SXin LI static bool dir_loop (struct comparison const *, int);
5018fd37a7SXin LI static int compare_names_for_qsort (void const *, void const *);
5118fd37a7SXin LI
5218fd37a7SXin LI
5318fd37a7SXin LI /* Read a directory and get its vector of names. */
5418fd37a7SXin LI
5518fd37a7SXin LI static bool
dir_read(struct file_data const * dir,struct dirdata * dirdata)5618fd37a7SXin LI dir_read (struct file_data const *dir, struct dirdata *dirdata)
5718fd37a7SXin LI {
5818fd37a7SXin LI register struct dirent *next;
5918fd37a7SXin LI register size_t i;
6018fd37a7SXin LI
6118fd37a7SXin LI /* Address of block containing the files that are described. */
6218fd37a7SXin LI char const **names;
6318fd37a7SXin LI
6418fd37a7SXin LI /* Number of files in directory. */
6518fd37a7SXin LI size_t nnames;
6618fd37a7SXin LI
6718fd37a7SXin LI /* Allocated and used storage for file name data. */
6818fd37a7SXin LI char *data;
6918fd37a7SXin LI size_t data_alloc, data_used;
7018fd37a7SXin LI
7118fd37a7SXin LI dirdata->names = 0;
7218fd37a7SXin LI dirdata->data = 0;
7318fd37a7SXin LI nnames = 0;
7418fd37a7SXin LI data = 0;
7518fd37a7SXin LI
7618fd37a7SXin LI if (dir->desc != -1)
7718fd37a7SXin LI {
7818fd37a7SXin LI /* Open the directory and check for errors. */
7918fd37a7SXin LI register DIR *reading = opendir (dir->name);
8018fd37a7SXin LI if (!reading)
8118fd37a7SXin LI return false;
8218fd37a7SXin LI
8318fd37a7SXin LI /* Initialize the table of filenames. */
8418fd37a7SXin LI
8518fd37a7SXin LI data_alloc = 512;
8618fd37a7SXin LI data_used = 0;
8718fd37a7SXin LI dirdata->data = data = xmalloc (data_alloc);
8818fd37a7SXin LI
8918fd37a7SXin LI /* Read the directory entries, and insert the subfiles
9018fd37a7SXin LI into the `data' table. */
9118fd37a7SXin LI
9218fd37a7SXin LI while ((errno = 0, (next = readdir (reading)) != 0))
9318fd37a7SXin LI {
9418fd37a7SXin LI char *d_name = next->d_name;
9518fd37a7SXin LI size_t d_size = NAMLEN (next) + 1;
9618fd37a7SXin LI
9718fd37a7SXin LI /* Ignore "." and "..". */
9818fd37a7SXin LI if (d_name[0] == '.'
9918fd37a7SXin LI && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0)))
10018fd37a7SXin LI continue;
10118fd37a7SXin LI
10218fd37a7SXin LI if (excluded_filename (excluded, d_name))
10318fd37a7SXin LI continue;
10418fd37a7SXin LI
10518fd37a7SXin LI while (data_alloc < data_used + d_size)
10618fd37a7SXin LI {
10718fd37a7SXin LI if (PTRDIFF_MAX / 2 <= data_alloc)
10818fd37a7SXin LI xalloc_die ();
10918fd37a7SXin LI dirdata->data = data = xrealloc (data, data_alloc *= 2);
11018fd37a7SXin LI }
11118fd37a7SXin LI
11218fd37a7SXin LI memcpy (data + data_used, d_name, d_size);
11318fd37a7SXin LI data_used += d_size;
11418fd37a7SXin LI nnames++;
11518fd37a7SXin LI }
11618fd37a7SXin LI if (errno)
11718fd37a7SXin LI {
11818fd37a7SXin LI int e = errno;
11918fd37a7SXin LI closedir (reading);
12018fd37a7SXin LI errno = e;
12118fd37a7SXin LI return false;
12218fd37a7SXin LI }
12318fd37a7SXin LI #if CLOSEDIR_VOID
12418fd37a7SXin LI closedir (reading);
12518fd37a7SXin LI #else
12618fd37a7SXin LI if (closedir (reading) != 0)
12718fd37a7SXin LI return false;
12818fd37a7SXin LI #endif
12918fd37a7SXin LI }
13018fd37a7SXin LI
13118fd37a7SXin LI /* Create the `names' table from the `data' table. */
13218fd37a7SXin LI if (PTRDIFF_MAX / sizeof *names - 1 <= nnames)
13318fd37a7SXin LI xalloc_die ();
13418fd37a7SXin LI dirdata->names = names = xmalloc ((nnames + 1) * sizeof *names);
13518fd37a7SXin LI dirdata->nnames = nnames;
13618fd37a7SXin LI for (i = 0; i < nnames; i++)
13718fd37a7SXin LI {
13818fd37a7SXin LI names[i] = data;
13918fd37a7SXin LI data += strlen (data) + 1;
14018fd37a7SXin LI }
14118fd37a7SXin LI names[nnames] = 0;
14218fd37a7SXin LI return true;
14318fd37a7SXin LI }
14418fd37a7SXin LI
14518fd37a7SXin LI /* Compare file names, returning a value compatible with strcmp. */
14618fd37a7SXin LI
14718fd37a7SXin LI static int
compare_names(char const * name1,char const * name2)14818fd37a7SXin LI compare_names (char const *name1, char const *name2)
14918fd37a7SXin LI {
15018fd37a7SXin LI if (locale_specific_sorting)
15118fd37a7SXin LI {
15218fd37a7SXin LI int r;
15318fd37a7SXin LI errno = 0;
15418fd37a7SXin LI if (ignore_file_name_case)
15518fd37a7SXin LI r = strcasecoll (name1, name2);
15618fd37a7SXin LI else
15718fd37a7SXin LI r = strcoll (name1, name2);
15818fd37a7SXin LI if (errno)
15918fd37a7SXin LI {
16018fd37a7SXin LI error (0, errno, _("cannot compare file names `%s' and `%s'"),
16118fd37a7SXin LI name1, name2);
16218fd37a7SXin LI longjmp (failed_locale_specific_sorting, 1);
16318fd37a7SXin LI }
16418fd37a7SXin LI return r;
16518fd37a7SXin LI }
16618fd37a7SXin LI
16718fd37a7SXin LI return (ignore_file_name_case
16818fd37a7SXin LI ? strcasecmp (name1, name2)
16918fd37a7SXin LI : file_name_cmp (name1, name2));
17018fd37a7SXin LI }
17118fd37a7SXin LI
17218fd37a7SXin LI /* A wrapper for compare_names suitable as an argument for qsort. */
17318fd37a7SXin LI
17418fd37a7SXin LI static int
compare_names_for_qsort(void const * file1,void const * file2)17518fd37a7SXin LI compare_names_for_qsort (void const *file1, void const *file2)
17618fd37a7SXin LI {
17718fd37a7SXin LI char const *const *f1 = file1;
17818fd37a7SXin LI char const *const *f2 = file2;
17918fd37a7SXin LI return compare_names (*f1, *f2);
18018fd37a7SXin LI }
18118fd37a7SXin LI
18218fd37a7SXin LI /* Compare the contents of two directories named in CMP.
18318fd37a7SXin LI This is a top-level routine; it does everything necessary for diff
18418fd37a7SXin LI on two directories.
18518fd37a7SXin LI
18618fd37a7SXin LI CMP->file[0].desc == -1 says directory CMP->file[0] doesn't exist,
18718fd37a7SXin LI but pretend it is empty. Likewise for CMP->file[1].
18818fd37a7SXin LI
18918fd37a7SXin LI HANDLE_FILE is a caller-provided subroutine called to handle each file.
19018fd37a7SXin LI It gets three operands: CMP, name of file in dir 0, name of file in dir 1.
19118fd37a7SXin LI These names are relative to the original working directory.
19218fd37a7SXin LI
19318fd37a7SXin LI For a file that appears in only one of the dirs, one of the name-args
19418fd37a7SXin LI to HANDLE_FILE is zero.
19518fd37a7SXin LI
19618fd37a7SXin LI Returns the maximum of all the values returned by HANDLE_FILE,
19718fd37a7SXin LI or EXIT_TROUBLE if trouble is encountered in opening files. */
19818fd37a7SXin LI
19918fd37a7SXin LI int
diff_dirs(struct comparison const * cmp,int (* handle_file)(struct comparison const *,char const *,char const *))20018fd37a7SXin LI diff_dirs (struct comparison const *cmp,
20118fd37a7SXin LI int (*handle_file) (struct comparison const *,
20218fd37a7SXin LI char const *, char const *))
20318fd37a7SXin LI {
20418fd37a7SXin LI struct dirdata dirdata[2];
20518fd37a7SXin LI int volatile val = EXIT_SUCCESS;
20618fd37a7SXin LI int i;
20718fd37a7SXin LI
20818fd37a7SXin LI if ((cmp->file[0].desc == -1 || dir_loop (cmp, 0))
20918fd37a7SXin LI && (cmp->file[1].desc == -1 || dir_loop (cmp, 1)))
21018fd37a7SXin LI {
21118fd37a7SXin LI error (0, 0, "%s: recursive directory loop",
21218fd37a7SXin LI cmp->file[cmp->file[0].desc == -1].name);
21318fd37a7SXin LI return EXIT_TROUBLE;
21418fd37a7SXin LI }
21518fd37a7SXin LI
21618fd37a7SXin LI /* Get contents of both dirs. */
21718fd37a7SXin LI for (i = 0; i < 2; i++)
21818fd37a7SXin LI if (! dir_read (&cmp->file[i], &dirdata[i]))
21918fd37a7SXin LI {
22018fd37a7SXin LI perror_with_name (cmp->file[i].name);
22118fd37a7SXin LI val = EXIT_TROUBLE;
22218fd37a7SXin LI }
22318fd37a7SXin LI
22418fd37a7SXin LI if (val == EXIT_SUCCESS)
22518fd37a7SXin LI {
22618fd37a7SXin LI char const **volatile names[2];
22718fd37a7SXin LI names[0] = dirdata[0].names;
22818fd37a7SXin LI names[1] = dirdata[1].names;
22918fd37a7SXin LI
23018fd37a7SXin LI /* Use locale-specific sorting if possible, else native byte order. */
23118fd37a7SXin LI locale_specific_sorting = true;
23218fd37a7SXin LI if (setjmp (failed_locale_specific_sorting))
23318fd37a7SXin LI locale_specific_sorting = false;
23418fd37a7SXin LI
23518fd37a7SXin LI /* Sort the directories. */
23618fd37a7SXin LI for (i = 0; i < 2; i++)
23718fd37a7SXin LI qsort (names[i], dirdata[i].nnames, sizeof *dirdata[i].names,
23818fd37a7SXin LI compare_names_for_qsort);
23918fd37a7SXin LI
24018fd37a7SXin LI /* If `-S name' was given, and this is the topmost level of comparison,
24118fd37a7SXin LI ignore all file names less than the specified starting name. */
24218fd37a7SXin LI
24318fd37a7SXin LI if (starting_file && ! cmp->parent)
24418fd37a7SXin LI {
24518fd37a7SXin LI while (*names[0] && compare_names (*names[0], starting_file) < 0)
24618fd37a7SXin LI names[0]++;
24718fd37a7SXin LI while (*names[1] && compare_names (*names[1], starting_file) < 0)
24818fd37a7SXin LI names[1]++;
24918fd37a7SXin LI }
25018fd37a7SXin LI
25118fd37a7SXin LI /* Loop while files remain in one or both dirs. */
25218fd37a7SXin LI while (*names[0] || *names[1])
25318fd37a7SXin LI {
25418fd37a7SXin LI /* Compare next name in dir 0 with next name in dir 1.
25518fd37a7SXin LI At the end of a dir,
25618fd37a7SXin LI pretend the "next name" in that dir is very large. */
25718fd37a7SXin LI int nameorder = (!*names[0] ? 1 : !*names[1] ? -1
25818fd37a7SXin LI : compare_names (*names[0], *names[1]));
25918fd37a7SXin LI int v1 = (*handle_file) (cmp,
26018fd37a7SXin LI 0 < nameorder ? 0 : *names[0]++,
26118fd37a7SXin LI nameorder < 0 ? 0 : *names[1]++);
26218fd37a7SXin LI if (val < v1)
26318fd37a7SXin LI val = v1;
26418fd37a7SXin LI }
26518fd37a7SXin LI }
26618fd37a7SXin LI
26718fd37a7SXin LI for (i = 0; i < 2; i++)
26818fd37a7SXin LI {
26918fd37a7SXin LI if (dirdata[i].names)
27018fd37a7SXin LI free (dirdata[i].names);
27118fd37a7SXin LI if (dirdata[i].data)
27218fd37a7SXin LI free (dirdata[i].data);
27318fd37a7SXin LI }
27418fd37a7SXin LI
27518fd37a7SXin LI return val;
27618fd37a7SXin LI }
27718fd37a7SXin LI
27818fd37a7SXin LI /* Return nonzero if CMP is looping recursively in argument I. */
27918fd37a7SXin LI
28018fd37a7SXin LI static bool
dir_loop(struct comparison const * cmp,int i)28118fd37a7SXin LI dir_loop (struct comparison const *cmp, int i)
28218fd37a7SXin LI {
28318fd37a7SXin LI struct comparison const *p = cmp;
28418fd37a7SXin LI while ((p = p->parent))
28518fd37a7SXin LI if (0 < same_file (&p->file[i].stat, &cmp->file[i].stat))
28618fd37a7SXin LI return true;
28718fd37a7SXin LI return false;
28818fd37a7SXin LI }
289