os/ossrv/glib/glib/gpattern.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /* GLIB - Library of useful routines for C programming
     2  * Copyright (C) 1995-1997, 1999  Peter Mattis, Red Hat, Inc.
     3  * Portions copyright (c) 2006-2009 Nokia Corporation.  All rights reserved.
     4  *
     5  * This library is free software; you can redistribute it and/or
     6  * modify it under the terms of the GNU Lesser General Public
     7  * License as published by the Free Software Foundation; either
     8  * version 2 of the License, or (at your option) any later version.
     9  *
    10  * This library is distributed in the hope that it will be useful,
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13  * Lesser General Public License for more details.
    14  *
    15  * You should have received a copy of the GNU Lesser General Public
    16  * License along with this library; if not, write to the
    17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    18  * Boston, MA 02111-1307, USA.
    19  */
    20 
    21 #include "config.h"
    22 
    23 #include <string.h>
    24 
    25 #include "gpattern.h"
    26 
    27 #include "gmacros.h"
    28 #include "gmessages.h"
    29 #include "gmem.h"
    30 #include "gunicode.h"
    31 #include "gutils.h" 
    32 #include "galias.h"
    33 
    34 /* keep enum and structure of gpattern.c and patterntest.c in sync */
    35 typedef enum
    36 {
    37   G_MATCH_ALL,       /* "*A?A*" */
    38   G_MATCH_ALL_TAIL,  /* "*A?AA" */
    39   G_MATCH_HEAD,      /* "AAAA*" */
    40   G_MATCH_TAIL,      /* "*AAAA" */
    41   G_MATCH_EXACT,     /* "AAAAA" */
    42   G_MATCH_LAST
    43 } GMatchType;
    44 
    45 struct _GPatternSpec
    46 {
    47   GMatchType match_type;
    48   guint      pattern_length;
    49   guint      min_length;
    50   guint      max_length;
    51   gchar     *pattern;
    52 };
    53 
    54 
    55 /* --- functions --- */
    56 static inline gboolean
    57 g_pattern_ph_match (const gchar *match_pattern,
    58 		    const gchar *match_string,
    59 		    gboolean    *wildcard_reached_p)
    60 {
    61   register const gchar *pattern, *string;
    62   register gchar ch;
    63 
    64   pattern = match_pattern;
    65   string = match_string;
    66 
    67   ch = *pattern;
    68   pattern++;
    69   while (ch)
    70     {
    71       switch (ch)
    72 	{
    73 	case '?':
    74 	  if (!*string)
    75 	    return FALSE;
    76 	  string = g_utf8_next_char (string);
    77 	  break;
    78 
    79 	case '*':
    80 	  *wildcard_reached_p = TRUE;
    81 	  do
    82 	    {
    83 	      ch = *pattern;
    84 	      pattern++;
    85 	      if (ch == '?')
    86 		{
    87 		  if (!*string)
    88 		    return FALSE;
    89 		  string = g_utf8_next_char (string);
    90 		}
    91 	    }
    92 	  while (ch == '*' || ch == '?');
    93 	  if (!ch)
    94 	    return TRUE;
    95 	  do
    96 	    {
    97               gboolean next_wildcard_reached = FALSE;
    98 	      while (ch != *string)
    99 		{
   100 		  if (!*string)
   101 		    return FALSE;
   102 		  string = g_utf8_next_char (string);
   103 		}
   104 	      string++;
   105 	      if (g_pattern_ph_match (pattern, string, &next_wildcard_reached))
   106 		return TRUE;
   107               if (next_wildcard_reached)
   108                 /* the forthcoming pattern substring up to the next wildcard has
   109                  * been matched, but a mismatch occoured for the rest of the
   110                  * pattern, following the next wildcard.
   111                  * there's no need to advance the current match position any
   112                  * further if the rest pattern will not match.
   113                  */
   114 		return FALSE;
   115 	    }
   116 	  while (*string);
   117 	  break;
   118 
   119 	default:
   120 	  if (ch == *string)
   121 	    string++;
   122 	  else
   123 	    return FALSE;
   124 	  break;
   125 	}
   126 
   127       ch = *pattern;
   128       pattern++;
   129     }
   130 
   131   return *string == 0;
   132 }
   133 
   134 EXPORT_C gboolean
   135 g_pattern_match (GPatternSpec *pspec,
   136 		 guint         string_length,
   137 		 const gchar  *string,
   138 		 const gchar  *string_reversed)
   139 {
   140   g_return_val_if_fail (pspec != NULL, FALSE);
   141   g_return_val_if_fail (string != NULL, FALSE);
   142 
   143   if (string_length < pspec->min_length ||
   144       string_length > pspec->max_length)
   145     return FALSE;
   146 
   147   switch (pspec->match_type)
   148     {
   149       gboolean dummy;
   150     case G_MATCH_ALL:
   151       return g_pattern_ph_match (pspec->pattern, string, &dummy);
   152     case G_MATCH_ALL_TAIL:
   153       if (string_reversed)
   154 	return g_pattern_ph_match (pspec->pattern, string_reversed, &dummy);
   155       else
   156 	{
   157           gboolean result;
   158           gchar *tmp;
   159 	  tmp = g_utf8_strreverse (string, string_length);
   160 	  result = g_pattern_ph_match (pspec->pattern, tmp, &dummy);
   161 	  g_free (tmp);
   162 	  return result;
   163 	}
   164     case G_MATCH_HEAD:
   165       if (pspec->pattern_length == string_length)
   166 	return strcmp (pspec->pattern, string) == 0;
   167       else if (pspec->pattern_length)
   168 	return strncmp (pspec->pattern, string, pspec->pattern_length) == 0;
   169       else
   170 	return TRUE;
   171     case G_MATCH_TAIL:
   172       if (pspec->pattern_length)
   173         return strcmp (pspec->pattern, string + (string_length - pspec->pattern_length)) == 0;
   174       else
   175 	return TRUE;
   176     case G_MATCH_EXACT:
   177       if (pspec->pattern_length != string_length)
   178         return FALSE;
   179       else
   180         return strcmp (pspec->pattern, string) == 0;
   181     default:
   182       g_return_val_if_fail (pspec->match_type < G_MATCH_LAST, FALSE);
   183       return FALSE;
   184     }
   185 }
   186 
   187 EXPORT_C GPatternSpec*
   188 g_pattern_spec_new (const gchar *pattern)
   189 {
   190   GPatternSpec *pspec;
   191   gboolean seen_joker = FALSE, seen_wildcard = FALSE, more_wildcards = FALSE;
   192   gint hw_pos = -1, tw_pos = -1, hj_pos = -1, tj_pos = -1;
   193   gboolean follows_wildcard = FALSE;
   194   guint pending_jokers = 0;
   195   const gchar *s;
   196   gchar *d;
   197   guint i;
   198   
   199   g_return_val_if_fail (pattern != NULL, NULL);
   200 
   201   /* canonicalize pattern and collect necessary stats */
   202   pspec = g_new (GPatternSpec, 1);
   203   pspec->pattern_length = strlen (pattern);
   204   pspec->min_length = 0;
   205   pspec->max_length = 0;
   206   pspec->pattern = g_new (gchar, pspec->pattern_length + 1);
   207   d = pspec->pattern;
   208   for (i = 0, s = pattern; *s != 0; s++)
   209     {
   210       switch (*s)
   211 	{
   212 	case '*':
   213 	  if (follows_wildcard)	/* compress multiple wildcards */
   214 	    {
   215 	      pspec->pattern_length--;
   216 	      continue;
   217 	    }
   218 	  follows_wildcard = TRUE;
   219 	  if (hw_pos < 0)
   220 	    hw_pos = i;
   221 	  tw_pos = i;
   222 	  break;
   223 	case '?':
   224 	  pending_jokers++;
   225 	  pspec->min_length++;
   226 	  pspec->max_length += 4; /* maximum UTF-8 character length */
   227 	  continue;
   228 	default:
   229 	  for (; pending_jokers; pending_jokers--, i++) {
   230 	    *d++ = '?';
   231   	    if (hj_pos < 0)
   232 	     hj_pos = i;
   233 	    tj_pos = i;
   234 	  }
   235 	  follows_wildcard = FALSE;
   236 	  pspec->min_length++;
   237 	  pspec->max_length++;
   238 	  break;
   239 	}
   240       *d++ = *s;
   241       i++;
   242     }
   243   for (; pending_jokers; pending_jokers--) {
   244     *d++ = '?';
   245     if (hj_pos < 0)
   246       hj_pos = i;
   247     tj_pos = i;
   248   }
   249   *d++ = 0;
   250   seen_joker = hj_pos >= 0;
   251   seen_wildcard = hw_pos >= 0;
   252   more_wildcards = seen_wildcard && hw_pos != tw_pos;
   253   if (seen_wildcard)
   254     pspec->max_length = G_MAXUINT;
   255 
   256   /* special case sole head/tail wildcard or exact matches */
   257   if (!seen_joker && !more_wildcards)
   258     {
   259       if (pspec->pattern[0] == '*')
   260 	{
   261 	  pspec->match_type = G_MATCH_TAIL;
   262           memmove (pspec->pattern, pspec->pattern + 1, --pspec->pattern_length);
   263 	  pspec->pattern[pspec->pattern_length] = 0;
   264 	  return pspec;
   265 	}
   266       if (pspec->pattern_length > 0 &&
   267 	  pspec->pattern[pspec->pattern_length - 1] == '*')
   268 	{
   269 	  pspec->match_type = G_MATCH_HEAD;
   270 	  pspec->pattern[--pspec->pattern_length] = 0;
   271 	  return pspec;
   272 	}
   273       if (!seen_wildcard)
   274 	{
   275 	  pspec->match_type = G_MATCH_EXACT;
   276 	  return pspec;
   277 	}
   278     }
   279 
   280   /* now just need to distinguish between head or tail match start */
   281   tw_pos = pspec->pattern_length - 1 - tw_pos;	/* last pos to tail distance */
   282   tj_pos = pspec->pattern_length - 1 - tj_pos;	/* last pos to tail distance */
   283   if (seen_wildcard)
   284     pspec->match_type = tw_pos > hw_pos ? G_MATCH_ALL_TAIL : G_MATCH_ALL;
   285   else /* seen_joker */
   286     pspec->match_type = tj_pos > hj_pos ? G_MATCH_ALL_TAIL : G_MATCH_ALL;
   287   if (pspec->match_type == G_MATCH_ALL_TAIL) {
   288     gchar *tmp = pspec->pattern;
   289     pspec->pattern = g_utf8_strreverse (pspec->pattern, pspec->pattern_length);
   290     g_free (tmp);
   291   }
   292   return pspec;
   293 }
   294 
   295 EXPORT_C void
   296 g_pattern_spec_free (GPatternSpec *pspec)
   297 {
   298   g_return_if_fail (pspec != NULL);
   299 
   300   g_free (pspec->pattern);
   301   g_free (pspec);
   302 }
   303 
   304 EXPORT_C gboolean
   305 g_pattern_spec_equal (GPatternSpec *pspec1,
   306 		      GPatternSpec *pspec2)
   307 {
   308   g_return_val_if_fail (pspec1 != NULL, FALSE);
   309   g_return_val_if_fail (pspec2 != NULL, FALSE);
   310 
   311   return (pspec1->pattern_length == pspec2->pattern_length &&
   312 	  pspec1->match_type == pspec2->match_type &&
   313 	  strcmp (pspec1->pattern, pspec2->pattern) == 0);
   314 }
   315 
   316 EXPORT_C gboolean
   317 g_pattern_match_string (GPatternSpec *pspec,
   318 			const gchar  *string)
   319 {
   320   g_return_val_if_fail (pspec != NULL, FALSE);
   321   g_return_val_if_fail (string != NULL, FALSE);
   322 
   323   return g_pattern_match (pspec, strlen (string), string, NULL);
   324 }
   325 
   326 EXPORT_C gboolean
   327 g_pattern_match_simple (const gchar *pattern,
   328 			const gchar *string)
   329 {
   330   GPatternSpec *pspec;
   331   gboolean ergo;
   332 
   333   g_return_val_if_fail (pattern != NULL, FALSE);
   334   g_return_val_if_fail (string != NULL, FALSE);
   335 
   336   pspec = g_pattern_spec_new (pattern);
   337   ergo = g_pattern_match (pspec, strlen (string), string, NULL);
   338   g_pattern_spec_free (pspec);
   339 
   340   return ergo;
   341 }
   342 
   343 #define __G_PATTERN_C__
   344 #include "galiasdef.c"