xref: /freebsd/contrib/diff/src/dir.c (revision 18fd37a72c3a7549d2d4f6c6ea00bdcd2bdaca01)
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