132785Speter/* Read, sort and compare two directories.  Used for GNU DIFF.
232785Speter   Copyright (C) 1988, 1989, 1992, 1993, 1994 Free Software Foundation, Inc.
332785Speter
432785SpeterThis file is part of GNU DIFF.
532785Speter
632785SpeterGNU DIFF is free software; you can redistribute it and/or modify
732785Speterit under the terms of the GNU General Public License as published by
832785Speterthe Free Software Foundation; either version 2, or (at your option)
932785Speterany later version.
1032785Speter
1132785SpeterGNU DIFF is distributed in the hope that it will be useful,
1232785Speterbut WITHOUT ANY WARRANTY; without even the implied warranty of
1332785SpeterMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1432785SpeterGNU General Public License for more details.
1532785Speter
1654427Speter*/
1732785Speter
1832785Speter#include "diff.h"
1932785Speter
2032785Speter/* Read the directory named by DIR and store into DIRDATA a sorted vector
2132785Speter   of filenames for its contents.  DIR->desc == -1 means this directory is
2232785Speter   known to be nonexistent, so set DIRDATA to an empty vector.
2332785Speter   Return -1 (setting errno) if error, 0 otherwise.  */
2432785Speter
2532785Speterstruct dirdata
2632785Speter{
2732785Speter  char const **names;	/* Sorted names of files in dir, 0-terminated.  */
2832785Speter  char *data;	/* Allocated storage for file names.  */
2932785Speter};
3032785Speter
3132785Speterstatic int compare_names PARAMS((void const *, void const *));
3232785Speterstatic int dir_sort PARAMS((struct file_data const *, struct dirdata *));
3332785Speter
3432785Speter#ifdef _WIN32
3532785Speter#define CLOSEDIR_VOID 1
3632785Speter#endif
3732785Speter
3832785Speterstatic int
3932785Speterdir_sort (dir, dirdata)
4032785Speter     struct file_data const *dir;
4132785Speter     struct dirdata *dirdata;
4232785Speter{
4332785Speter  register struct dirent *next;
4432785Speter  register int i;
4532785Speter
4632785Speter  /* Address of block containing the files that are described.  */
4732785Speter  char const **names;
4832785Speter
4932785Speter  /* Number of files in directory.  */
5032785Speter  size_t nnames;
5132785Speter
5232785Speter  /* Allocated and used storage for file name data.  */
5332785Speter  char *data;
5432785Speter  size_t data_alloc, data_used;
5532785Speter
5632785Speter  dirdata->names = 0;
5732785Speter  dirdata->data = 0;
5832785Speter  nnames = 0;
5932785Speter  data = 0;
6032785Speter
6132785Speter  if (dir->desc != -1)
6232785Speter    {
6332785Speter      /* Open the directory and check for errors.  */
6481404Speter      register DIR *reading = CVS_OPENDIR (dir->name);
6532785Speter      if (!reading)
6632785Speter	return -1;
6732785Speter
6832785Speter      /* Initialize the table of filenames.  */
6932785Speter
7032785Speter      data_alloc = max (1, (size_t) dir->stat.st_size);
7132785Speter      data_used = 0;
7232785Speter      dirdata->data = data = xmalloc (data_alloc);
7332785Speter
7432785Speter      /* Read the directory entries, and insert the subfiles
7532785Speter	 into the `data' table.  */
7632785Speter
7781404Speter      while ((errno = 0, (next = CVS_READDIR (reading)) != 0))
7832785Speter	{
7932785Speter	  char *d_name = next->d_name;
8032785Speter	  size_t d_size = NAMLEN (next) + 1;
8132785Speter
8232785Speter	  /* Ignore the files `.' and `..' */
8332785Speter	  if (d_name[0] == '.'
8432785Speter	      && (d_name[1] == 0 || (d_name[1] == '.' && d_name[2] == 0)))
8532785Speter	    continue;
8632785Speter
8732785Speter	  if (excluded_filename (d_name))
8832785Speter	    continue;
8932785Speter
9032785Speter	  while (data_alloc < data_used + d_size)
9132785Speter	    dirdata->data = data = xrealloc (data, data_alloc *= 2);
9232785Speter	  memcpy (data + data_used, d_name, d_size);
9332785Speter	  data_used += d_size;
9432785Speter	  nnames++;
9532785Speter	}
9632785Speter      if (errno)
9732785Speter	{
9832785Speter	  int e = errno;
9981404Speter	  CVS_CLOSEDIR (reading);
10032785Speter	  errno = e;
10132785Speter	  return -1;
10232785Speter	}
10332785Speter#if CLOSEDIR_VOID
10481404Speter      CVS_CLOSEDIR (reading);
10532785Speter#else
10681404Speter      if (CVS_CLOSEDIR (reading) != 0)
10732785Speter	return -1;
10832785Speter#endif
10932785Speter    }
11032785Speter
11132785Speter  /* Create the `names' table from the `data' table.  */
11232785Speter  dirdata->names = names = (char const **) xmalloc (sizeof (char *)
11332785Speter						    * (nnames + 1));
11432785Speter  for (i = 0;  i < nnames;  i++)
11532785Speter    {
11632785Speter      names[i] = data;
11732785Speter      data += strlen (data) + 1;
11832785Speter    }
11932785Speter  names[nnames] = 0;
12032785Speter
12132785Speter  /* Sort the table.  */
12232785Speter  qsort (names, nnames, sizeof (char *), compare_names);
12332785Speter
12432785Speter  return 0;
12532785Speter}
12632785Speter
12732785Speter/* Sort the files now in the table.  */
12832785Speter
12932785Speterstatic int
13032785Spetercompare_names (file1, file2)
13132785Speter     void const *file1, *file2;
13232785Speter{
13332785Speter  return filename_cmp (* (char const *const *) file1,
13432785Speter		       * (char const *const *) file2);
13532785Speter}
13632785Speter
13732785Speter/* Compare the contents of two directories named in FILEVEC[0] and FILEVEC[1].
13832785Speter   This is a top-level routine; it does everything necessary for diff
13932785Speter   on two directories.
14032785Speter
14132785Speter   FILEVEC[0].desc == -1 says directory FILEVEC[0] doesn't exist,
14232785Speter   but pretend it is empty.  Likewise for FILEVEC[1].
14332785Speter
14432785Speter   HANDLE_FILE is a caller-provided subroutine called to handle each file.
14532785Speter   It gets five operands: dir and name (rel to original working dir) of file
14632785Speter   in dir 0, dir and name pathname of file in dir 1, and the recursion depth.
14732785Speter
14832785Speter   For a file that appears in only one of the dirs, one of the name-args
14932785Speter   to HANDLE_FILE is zero.
15032785Speter
15132785Speter   DEPTH is the current depth in recursion, used for skipping top-level
15232785Speter   files by the -S option.
15332785Speter
15432785Speter   Returns the maximum of all the values returned by HANDLE_FILE,
15532785Speter   or 2 if trouble is encountered in opening files.  */
15632785Speter
15732785Speterint
15832785Speterdiff_dirs (filevec, handle_file, depth)
15932785Speter     struct file_data const filevec[];
16032785Speter     int (*handle_file) PARAMS((char const *, char const *, char const *, char const *, int));
16132785Speter     int depth;
16232785Speter{
16332785Speter  struct dirdata dirdata[2];
16432785Speter  int val = 0;			/* Return value.  */
16532785Speter  int i;
16632785Speter
16732785Speter  /* Get sorted contents of both dirs.  */
16832785Speter  for (i = 0; i < 2; i++)
16932785Speter    if (dir_sort (&filevec[i], &dirdata[i]) != 0)
17032785Speter      {
17132785Speter	perror_with_name (filevec[i].name);
17232785Speter	val = 2;
17332785Speter      }
17432785Speter
17532785Speter  if (val == 0)
17632785Speter    {
17732785Speter      register char const * const *names0 = dirdata[0].names;
17832785Speter      register char const * const *names1 = dirdata[1].names;
17932785Speter      char const *name0 = filevec[0].name;
18032785Speter      char const *name1 = filevec[1].name;
18132785Speter
18232785Speter      /* If `-S name' was given, and this is the topmost level of comparison,
18332785Speter	 ignore all file names less than the specified starting name.  */
18432785Speter
18532785Speter      if (dir_start_file && depth == 0)
18632785Speter	{
18732785Speter	  while (*names0 && filename_cmp (*names0, dir_start_file) < 0)
18832785Speter	    names0++;
18932785Speter	  while (*names1 && filename_cmp (*names1, dir_start_file) < 0)
19032785Speter	    names1++;
19132785Speter	}
19232785Speter
19332785Speter      /* Loop while files remain in one or both dirs.  */
19432785Speter      while (*names0 || *names1)
19532785Speter	{
19632785Speter	  /* Compare next name in dir 0 with next name in dir 1.
19732785Speter	     At the end of a dir,
19832785Speter	     pretend the "next name" in that dir is very large.  */
19932785Speter	  int nameorder = (!*names0 ? 1 : !*names1 ? -1
20032785Speter			   : filename_cmp (*names0, *names1));
20132785Speter	  int v1 = (*handle_file) (name0, 0 < nameorder ? 0 : *names0++,
20232785Speter				   name1, nameorder < 0 ? 0 : *names1++,
20332785Speter				   depth + 1);
20432785Speter	  if (v1 > val)
20532785Speter	    val = v1;
20632785Speter	}
20732785Speter    }
20832785Speter
20932785Speter  for (i = 0; i < 2; i++)
21032785Speter    {
21132785Speter      if (dirdata[i].names)
21232785Speter	free (dirdata[i].names);
21332785Speter      if (dirdata[i].data)
21432785Speter	free (dirdata[i].data);
21532785Speter    }
21632785Speter
21732785Speter  return val;
21832785Speter}
219