xref: /openbsd-src/gnu/usr.bin/cvs/diff/dir.c (revision 43c1707e6f6829177cb1974ee6615ce6c1307689)
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