12286d8edStholo /* Read, sort and compare two directories. Used for GNU DIFF.
22286d8edStholo Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
32286d8edStholo
42286d8edStholo This file is part of GNU DIFF.
52286d8edStholo
62286d8edStholo GNU DIFF is free software; you can redistribute it and/or modify
72286d8edStholo it under the terms of the GNU General Public License as published by
82286d8edStholo the Free Software Foundation; either version 2, or (at your option)
92286d8edStholo any later version.
102286d8edStholo
112286d8edStholo GNU DIFF is distributed in the hope that it will be useful,
122286d8edStholo but WITHOUT ANY WARRANTY; without even the implied warranty of
132286d8edStholo MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
142286d8edStholo GNU General Public License for more details.
152286d8edStholo
16c71bc7e2Stholo */
172286d8edStholo
182286d8edStholo #include "diff.h"
192286d8edStholo
202286d8edStholo /* Read the directory named by DIR and store into DIRDATA a sorted vector
212286d8edStholo of filenames for its contents. DIR->desc == -1 means this directory is
222286d8edStholo known to be nonexistent, so set DIRDATA to an empty vector.
232286d8edStholo Return -1 (setting errno) if error, 0 otherwise. */
242286d8edStholo
252286d8edStholo struct dirdata
262286d8edStholo {
272286d8edStholo char const **names; /* Sorted names of files in dir, 0-terminated. */
282286d8edStholo char *data; /* Allocated storage for file names. */
292286d8edStholo };
302286d8edStholo
312286d8edStholo static int compare_names PARAMS((void const *, void const *));
322286d8edStholo static int dir_sort PARAMS((struct file_data const *, struct dirdata *));
332286d8edStholo
342286d8edStholo #ifdef _WIN32
352286d8edStholo #define CLOSEDIR_VOID 1
362286d8edStholo #endif
372286d8edStholo
382286d8edStholo static int
dir_sort(dir,dirdata)392286d8edStholo dir_sort (dir, dirdata)
402286d8edStholo struct file_data const *dir;
412286d8edStholo struct dirdata *dirdata;
422286d8edStholo {
432286d8edStholo register struct dirent *next;
442286d8edStholo register int i;
452286d8edStholo
462286d8edStholo /* Address of block containing the files that are described. */
472286d8edStholo char const **names;
482286d8edStholo
492286d8edStholo /* Number of files in directory. */
502286d8edStholo size_t nnames;
512286d8edStholo
522286d8edStholo /* Allocated and used storage for file name data. */
532286d8edStholo char *data;
542286d8edStholo size_t data_alloc, data_used;
552286d8edStholo
562286d8edStholo dirdata->names = 0;
572286d8edStholo dirdata->data = 0;
582286d8edStholo nnames = 0;
592286d8edStholo data = 0;
602286d8edStholo
612286d8edStholo if (dir->desc != -1)
622286d8edStholo {
632286d8edStholo /* Open the directory and check for errors. */
64*43c1707eStholo register DIR *reading = CVS_OPENDIR (dir->name);
652286d8edStholo if (!reading)
662286d8edStholo return -1;
672286d8edStholo
682286d8edStholo /* Initialize the table of filenames. */
692286d8edStholo
702286d8edStholo data_alloc = max (1, (size_t) dir->stat.st_size);
712286d8edStholo data_used = 0;
722286d8edStholo dirdata->data = data = xmalloc (data_alloc);
732286d8edStholo
742286d8edStholo /* Read the directory entries, and insert the subfiles
752286d8edStholo into the `data' table. */
762286d8edStholo
77*43c1707eStholo while ((errno = 0, (next = CVS_READDIR (reading)) != 0))
782286d8edStholo {
792286d8edStholo char *d_name = next->d_name;
802286d8edStholo size_t d_size = NAMLEN (next) + 1;
812286d8edStholo
822286d8edStholo /* Ignore the files `.' and `..' */
832286d8edStholo if (d_name[0] == '.'
842286d8edStholo && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0)))
852286d8edStholo continue;
862286d8edStholo
872286d8edStholo if (excluded_filename (d_name))
882286d8edStholo continue;
892286d8edStholo
902286d8edStholo while (data_alloc < data_used + d_size)
912286d8edStholo dirdata->data = data = xrealloc (data, data_alloc *= 2);
922286d8edStholo memcpy (data + data_used, d_name, d_size);
932286d8edStholo data_used += d_size;
942286d8edStholo nnames++;
952286d8edStholo }
962286d8edStholo if (errno)
972286d8edStholo {
982286d8edStholo int e = errno;
99*43c1707eStholo CVS_CLOSEDIR (reading);
1002286d8edStholo errno = e;
1012286d8edStholo return -1;
1022286d8edStholo }
1032286d8edStholo #if CLOSEDIR_VOID
104*43c1707eStholo CVS_CLOSEDIR (reading);
1052286d8edStholo #else
106*43c1707eStholo if (CVS_CLOSEDIR (reading) != 0)
1072286d8edStholo return -1;
1082286d8edStholo #endif
1092286d8edStholo }
1102286d8edStholo
1112286d8edStholo /* Create the `names' table from the `data' table. */
1122286d8edStholo dirdata->names = names = (char const **) xmalloc (sizeof (char *)
1132286d8edStholo * (nnames + 1));
1142286d8edStholo for (i = 0; i < nnames; i++)
1152286d8edStholo {
1162286d8edStholo names[i] = data;
1172286d8edStholo data += strlen (data) + 1;
1182286d8edStholo }
1192286d8edStholo names[nnames] = 0;
1202286d8edStholo
1212286d8edStholo /* Sort the table. */
1222286d8edStholo qsort (names, nnames, sizeof (char *), compare_names);
1232286d8edStholo
1242286d8edStholo return 0;
1252286d8edStholo }
1262286d8edStholo
1272286d8edStholo /* Sort the files now in the table. */
1282286d8edStholo
1292286d8edStholo static int
compare_names(file1,file2)1302286d8edStholo compare_names (file1, file2)
1312286d8edStholo void const *file1, *file2;
1322286d8edStholo {
1332286d8edStholo return filename_cmp (* (char const *const *) file1,
1342286d8edStholo * (char const *const *) file2);
1352286d8edStholo }
1362286d8edStholo
1372286d8edStholo /* Compare the contents of two directories named in FILEVEC[0] and FILEVEC[1].
1382286d8edStholo This is a top-level routine; it does everything necessary for diff
1392286d8edStholo on two directories.
1402286d8edStholo
1412286d8edStholo FILEVEC[0].desc == -1 says directory FILEVEC[0] doesn't exist,
1422286d8edStholo but pretend it is empty. Likewise for FILEVEC[1].
1432286d8edStholo
1442286d8edStholo HANDLE_FILE is a caller-provided subroutine called to handle each file.
1452286d8edStholo It gets five operands: dir and name (rel to original working dir) of file
1462286d8edStholo in dir 0, dir and name pathname of file in dir 1, and the recursion depth.
1472286d8edStholo
1482286d8edStholo For a file that appears in only one of the dirs, one of the name-args
1492286d8edStholo to HANDLE_FILE is zero.
1502286d8edStholo
1512286d8edStholo DEPTH is the current depth in recursion, used for skipping top-level
1522286d8edStholo files by the -S option.
1532286d8edStholo
1542286d8edStholo Returns the maximum of all the values returned by HANDLE_FILE,
1552286d8edStholo or 2 if trouble is encountered in opening files. */
1562286d8edStholo
1572286d8edStholo int
diff_dirs(filevec,handle_file,depth)1582286d8edStholo diff_dirs (filevec, handle_file, depth)
1592286d8edStholo struct file_data const filevec[];
1602286d8edStholo int (*handle_file) PARAMS((char const *, char const *, char const *, char const *, int));
1612286d8edStholo int depth;
1622286d8edStholo {
1632286d8edStholo struct dirdata dirdata[2];
1642286d8edStholo int val = 0; /* Return value. */
1652286d8edStholo int i;
1662286d8edStholo
1672286d8edStholo /* Get sorted contents of both dirs. */
1682286d8edStholo for (i = 0; i < 2; i++)
1692286d8edStholo if (dir_sort (&filevec[i], &dirdata[i]) != 0)
1702286d8edStholo {
1712286d8edStholo perror_with_name (filevec[i].name);
1722286d8edStholo val = 2;
1732286d8edStholo }
1742286d8edStholo
1752286d8edStholo if (val == 0)
1762286d8edStholo {
1772286d8edStholo register char const * const *names0 = dirdata[0].names;
1782286d8edStholo register char const * const *names1 = dirdata[1].names;
1792286d8edStholo char const *name0 = filevec[0].name;
1802286d8edStholo char const *name1 = filevec[1].name;
1812286d8edStholo
1822286d8edStholo /* If `-S name' was given, and this is the topmost level of comparison,
1832286d8edStholo ignore all file names less than the specified starting name. */
1842286d8edStholo
1852286d8edStholo if (dir_start_file && depth == 0)
1862286d8edStholo {
1872286d8edStholo while (*names0 && filename_cmp (*names0, dir_start_file) < 0)
1882286d8edStholo names0++;
1892286d8edStholo while (*names1 && filename_cmp (*names1, dir_start_file) < 0)
1902286d8edStholo names1++;
1912286d8edStholo }
1922286d8edStholo
1932286d8edStholo /* Loop while files remain in one or both dirs. */
1942286d8edStholo while (*names0 || *names1)
1952286d8edStholo {
1962286d8edStholo /* Compare next name in dir 0 with next name in dir 1.
1972286d8edStholo At the end of a dir,
1982286d8edStholo pretend the "next name" in that dir is very large. */
1992286d8edStholo int nameorder = (!*names0 ? 1 : !*names1 ? -1
2002286d8edStholo : filename_cmp (*names0, *names1));
2012286d8edStholo int v1 = (*handle_file) (name0, 0 < nameorder ? 0 : *names0++,
2022286d8edStholo name1, nameorder < 0 ? 0 : *names1++,
2032286d8edStholo depth + 1);
2042286d8edStholo if (v1 > val)
2052286d8edStholo val = v1;
2062286d8edStholo }
2072286d8edStholo }
2082286d8edStholo
2092286d8edStholo for (i = 0; i < 2; i++)
2102286d8edStholo {
2112286d8edStholo if (dirdata[i].names)
2122286d8edStholo free (dirdata[i].names);
2132286d8edStholo if (dirdata[i].data)
2142286d8edStholo free (dirdata[i].data);
2152286d8edStholo }
2162286d8edStholo
2172286d8edStholo return val;
2182286d8edStholo }
219