From: skip@skipsun.UUCP (Skip Gilbrech)
Subject: Patches for find-3.7 to enable case-insensitive filename matching
Organization: GNUs Not Usenet
Distribution: gnu
Date: Tue, 25 Aug 1992 05:28:11 GMT

Following is a set of patches for find-3.7 which implement a -nocase
option that allows case-insensitive shell pattern matching in
conjunction with the -name, -lname, and -path predicates.

Case-insensitive matching could be done before, but only by explicitly
specifying the upper/lower case letters to match ([Aa][Bb][Cc] ...).

Some systems apparently provide a `fnmatch()' call already, and
lib/fnmatch.c had to be modified, and must be used, for this to work,
so I changed configure to unconditionally include it in LIBOBJS.  The
change probably should be made to `configure.in', but since I don't
have `autoconf' (mentioned in INSTALL), I couldn't do that.

My hope was that these changes, or equivalent functionality, might be
integrated into the base find distribution.  I've submitted similar
patches for earlier versions (2.2 and 3.5) without that happening,
though, and have received several requests for an update, so I'm
providing these patches to enable those who like the -nocase option to
implement it independently.

-Skip Gilbrech

######################################################################
Diffs follow:

*** configure-dist	Mon Jul 20 02:05:20 1992
--- configure	Mon Aug 24 21:56:09 1992
***************
*** 449,455 ****
  fi
  rm -f conftest*
  
! for func in fnmatch memset stpcpy strdup strftime strspn strstr strtol
  do
  echo checking for ${func}
  echo "
--- 449,457 ----
  fi
  rm -f conftest*
  
! LIBOBJS="fnmatch.o"
! 
! for func in memset stpcpy strdup strftime strspn strstr strtol
  do
  echo checking for ${func}
  echo "
*** lib/fnmatch.h-dist	Thu May 28 17:36:19 1992
--- lib/fnmatch.h	Mon Aug 24 22:31:57 1992
***************
*** 38,44 ****
  #define	FNM_PATHNAME	(1 << 0)/* No wildcard can ever match `/'.  */
  #define	FNM_NOESCAPE	(1 << 1)/* Backslashes don't quote special chars.  */
  #define	FNM_PERIOD	(1 << 2)/* Leading `.' is matched only explicitly.  */
! #define	__FNM_FLAGS	(FNM_PATHNAME|FNM_NOESCAPE|FNM_PERIOD)
  
  #if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_BSD_SOURCE)
  #define	FNM_FILE_NAME	FNM_PATHNAME
--- 38,45 ----
  #define	FNM_PATHNAME	(1 << 0)/* No wildcard can ever match `/'.  */
  #define	FNM_NOESCAPE	(1 << 1)/* Backslashes don't quote special chars.  */
  #define	FNM_PERIOD	(1 << 2)/* Leading `.' is matched only explicitly.  */
! #define	FNM_ANYCASE	(1 << 3)/* Matches ignore case. */
! #define	__FNM_FLAGS	(FNM_PATHNAME|FNM_NOESCAPE|FNM_PERIOD|FNM_ANYCASE)
  
  #if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_BSD_SOURCE)
  #define	FNM_FILE_NAME	FNM_PATHNAME
*** lib/fnmatch.c-dist	Wed Jun  3 19:51:40 1992
--- lib/fnmatch.c	Mon Aug 24 22:26:28 1992
***************
*** 26,31 ****
--- 26,40 ----
  #define const
  #endif
  
+ #include <ctype.h>
+ 
+ #define CHAR_SET_SIZE  256
+ static char ci_tbl[CHAR_SET_SIZE];
+ static int ci_tbl_initialized = 0;
+ 
+ #define CI_LOWER(c)	(ci_tbl[ (c) & (CHAR_SET_SIZE - 1) ])
+ #define CI_MATCH(c1,c2)	(CI_LOWER(c1) == CI_LOWER(c2))
+ 
  /* Match STRING against the filename pattern PATTERN, returning zero if
     it matches, nonzero if not.  */
  int
***************
*** 43,48 ****
--- 52,68 ----
        return -1;
      }
  
+   if ((flags & FNM_ANYCASE) && ! ci_tbl_initialized)
+   {
+       unsigned count;
+       
+       /* Map uppercase characters to corresponding lowercase ones.  */
+       for (count = 0; count < CHAR_SET_SIZE; ++count)
+ 	  ci_tbl[count] = isupper (count) ? tolower (count) : count;
+ 
+       ci_tbl_initialized = 1;
+   }
+ 
    while ((c = *p++) != '\0')
      {
        switch (c)
***************
*** 60,66 ****
  	case '\\':
  	  if (!(flags & FNM_NOESCAPE))
  	    c = *p++;
! 	  if (*n != c)
  	    return FNM_NOMATCH;
  	  break;
  
--- 80,86 ----
  	case '\\':
  	  if (!(flags & FNM_NOESCAPE))
  	    c = *p++;
! 	  if ((flags & FNM_ANYCASE) ? !CI_MATCH(*n, c) : *n != c)
  	    return FNM_NOMATCH;
  	  break;
  
***************
*** 80,86 ****
  	  {
  	    char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
  	    for (--p; *n != '\0'; ++n)
! 	      if ((c == '[' || *n == c1) &&
  		  fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
  		return 0;
  	    return FNM_NOMATCH;
--- 100,107 ----
  	  {
  	    char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
  	    for (--p; *n != '\0'; ++n)
! 	      if ((c == '[' ||
! 		   ((flags & FNM_ANYCASE) ? CI_MATCH(*n, c1) : *n == c1)) &&
  		  fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
  		return 0;
  	    return FNM_NOMATCH;
***************
*** 130,136 ****
  		    c = *p++;
  		  }
  
! 		if (*n >= cstart && *n <= cend)
  		  goto matched;
  
  		if (c == ']')
--- 151,164 ----
  		    c = *p++;
  		  }
  
! 		if (flags & FNM_ANYCASE)
! 		  {
! 		    if (CI_LOWER(*n) >= CI_LOWER(cstart) &&
! 			CI_LOWER(*n) <= CI_LOWER(cend))
! 
! 		      goto matched;
! 		  }
! 		else if (*n >= cstart && *n <= cend)
  		  goto matched;
  
  		if (c == ']')
***************
*** 159,165 ****
  	  break;
  
  	default:
! 	  if (c != *n)
  	    return FNM_NOMATCH;
  	}
  
--- 187,193 ----
  	  break;
  
  	default:
! 	  if ((flags & FNM_ANYCASE) ? !CI_MATCH(c, *n) : c != *n)
  	    return FNM_NOMATCH;
  	}
  
*** find/pred.c-dist	Mon Jul 13 22:09:20 1992
--- find/pred.c	Mon Aug 24 23:37:30 1992
***************
*** 123,128 ****
--- 123,129 ----
  boolean pred_name ();
  boolean pred_negate ();
  boolean pred_newer ();
+ /* no pred_nocase */
  /* no pred_noleaf */
  boolean pred_nogroup ();
  boolean pred_nouser ();
***************
*** 872,878 ****
        else
  	{
  	  linkname[linklen] = '\0';
! 	  if (fnmatch (pred_ptr->args.str, linkname, 0) == 0)
  	    ret = true;
  	}
        free (linkname);
--- 873,880 ----
        else
  	{
  	  linkname[linklen] = '\0';
! 	  if (fnmatch (pred_ptr->args.str, linkname,
! 		       case_insensitive ? FNM_ANYCASE : 0) == 0)
  	    ret = true;
  	}
        free (linkname);
***************
*** 951,957 ****
    char *base;
  
    base = basename (pathname);
!   if (fnmatch (pred_ptr->args.str, base, FNM_PERIOD) == 0)
      return (true);
    return (false);
  }
--- 953,960 ----
    char *base;
  
    base = basename (pathname);
!   if (fnmatch (pred_ptr->args.str, base,
! 	       case_insensitive ? (FNM_ANYCASE|FNM_PERIOD) : FNM_PERIOD) == 0)
      return (true);
    return (false);
  }
***************
*** 1084,1090 ****
       struct stat *stat_buf;
       struct predicate *pred_ptr;
  {
!   if (fnmatch (pred_ptr->args.str, pathname, 0) == 0)
      return (true);
    return (false);
  }
--- 1087,1094 ----
       struct stat *stat_buf;
       struct predicate *pred_ptr;
  {
!   if (fnmatch (pred_ptr->args.str, pathname,
! 	       case_insensitive ? FNM_ANYCASE : 0) == 0)
      return (true);
    return (false);
  }
*** find/find.c-dist	Fri Apr 24 02:55:34 1992
--- find/find.c	Mon Aug 24 22:41:44 1992
***************
*** 103,108 ****
--- 103,112 ----
  /* Pointer to the function used to stat files. */
  int (*xstat) ();
  
+ /* If true, enables case-insensitive filename searching.
+    Set by -nocase. */
+ boolean case_insensitive;
+ 
  #ifdef DEBUG_STAT
  int
  debug_stat (file, bufp)
***************
*** 135,140 ****
--- 139,145 ----
    no_leaf_check = false;
    stay_on_filesystem = false;
    exit_status = 0;
+   case_insensitive = false;
  #ifdef DEBUG_STAT
    xstat = debug_stat;
  #else /* !DEBUG_STAT */
*** find/defs.h-dist	Tue Jul 14 00:21:55 1992
--- find/defs.h	Mon Aug 24 21:56:07 1992
***************
*** 201,203 ****
--- 201,204 ----
  extern int exit_status;
  extern int path_length;
  extern int (*xstat) ();
+ extern boolean case_insensitive;
*** find/parser.c-dist	Mon Jul 13 13:04:31 1992
--- find/parser.c	Mon Aug 24 23:27:54 1992
***************
*** 98,103 ****
--- 98,104 ----
  boolean parse_name ();
  boolean parse_negate ();
  boolean parse_newer ();
+ boolean parse_nocase ();
  boolean parse_noleaf ();
  boolean parse_nogroup ();
  boolean parse_nouser ();
***************
*** 153,158 ****
--- 154,160 ----
  boolean pred_name ();
  boolean pred_negate ();
  boolean pred_newer ();
+ /* no pred_nocase */
  /* no pred_noleaf */
  boolean pred_nogroup ();
  boolean pred_nouser ();
***************
*** 245,250 ****
--- 247,253 ----
    {"ncpio", parse_ncpio},	/* Unix */
  #endif
    {"newer", parse_newer},
+   {"nocase", parse_nocase},	/* GNU (nocase patch - sg) */
    {"noleaf", parse_noleaf},	/* GNU */
    {"nogroup", parse_nogroup},
    {"nouser", parse_nouser},
***************
*** 768,773 ****
--- 771,785 ----
    our_pred->args.time = stat_newer.st_mtime;
    (*arg_ptr)++;
    return (true);
+ }
+ 
+ boolean
+ parse_nocase (argv, arg_ptr)
+      char *argv[];
+      int *arg_ptr;
+ {
+   case_insensitive = true;
+   return true;
  }
  
  boolean
*** man/find.1-dist	Tue May 12 04:43:31 1992
--- man/find.1	Mon Aug 24 23:41:15 1992
***************
*** 53,58 ****
--- 53,61 ----
  Do not apply any tests or actions at levels less than \fIlevels\fR (a
  non-negative integer).  `\-mindepth 1' means process all files except
  the command line arguments.
+ .IP \-nocase
+ Shell patterns used with \-name, \-lname, and \-path are matched without
+ regard to case.
  .IP "\-noleaf"
  Do not optimize by assuming that directories contain 2 fewer
  subdirectories than their hard link count.  Each directory on a normal


-- 
Skip Gilbrech			email:	skip@fsg.com
Fusion Systems Group			uupsi!fsg!skip
225 Broadway, 24th Floor		uupsi!skipnyc!skip
New York, NY 10007		phones:	212-285-8001 (work)
					201-825-8732 (home)

Newsgroups: gnu.utils.bug