CSL  5.2
pattern_match.c
Go to the documentation of this file.
1 /* Open SoundControl kit in C++ */
2 /* Copyright (C) 2002-2004 libOSC++ contributors. See AUTHORS */
3 /* */
4 /* This library is free software; you can redistribute it and/or */
5 /* modify it under the terms of the GNU Lesser General Public */
6 /* License as published by the Free Software Foundation; either */
7 /* version 2.1 of the License, or (at your option) any later version. */
8 /* */
9 /* This library is distributed in the hope that it will be useful, */
10 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
11 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
12 /* Lesser General Public License for more details. */
13 /* */
14 /* You should have received a copy of the GNU Lesser General Public */
15 /* License along with this library; if not, write to the Free Software */
16 /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17 /* */
18 /* For questions regarding this program contact */
19 /* Daniel Holth <dholth@fastmail.fm> or visit */
20 /* http://wiretap.stetson.edu/ */
21 
22 /* In the sprit of the public domain, my modifications to this file are also */
23 /* dedicated to the public domain. Daniel Holth, Oct. 2004 */
24 
25 /* ChangeLog:
26  *
27  * 2004-10-29 Import, convert to C++, begin OSC syntax changes. -dwh
28  * OSC syntax changes are now working, needs more testing.
29  *
30  */
31 
32 // Original header and syntax:
33 
34 /*
35  * robust glob pattern matcher
36  * ozan s. yigit/dec 1994
37  * public domain
38  *
39  * glob patterns:
40  * * matches zero or more characters
41  * ? matches any single character
42  * [set] matches any character in the set
43  * [^set] matches any character NOT in the set
44  * where a set is a group of characters or ranges. a range
45  * is written as two characters seperated with a hyphen: a-z denotes
46  * all characters between a to z inclusive.
47  * [-set] set matches a literal hypen and any character in the set
48  * []set] matches a literal close bracket and any character in the set
49  *
50  * char matches itself except where char is '*' or '?' or '['
51  * \char matches char, including any pattern character
52  *
53  * examples:
54  * a*c ac abc abbc ...
55  * a?c acc abc aXc ...
56  * a[a-z]c aac abc acc ...
57  * a[-a-z]c a-c aac abc ...
58  *
59  * $Log$
60  * Revision 1.1 2004/11/19 23:00:57 theno23
61  * Added lo_send_timestamped
62  *
63  * Revision 1.3 1995/09/14 23:24:23 oz
64  * removed boring test/main code.
65  *
66  * Revision 1.2 94/12/11 10:38:15 oz
67  * cset code fixed. it is now robust and interprets all
68  * variations of cset [i think] correctly, including [z-a] etc.
69  *
70  * Revision 1.1 94/12/08 12:45:23 oz
71  * Initial revision
72  */
73 
74 #include "lo/lo.h"
75 
76 #ifndef NEGATE
77 #define NEGATE '!'
78 #endif
79 
80 #ifndef true
81 #define true 1
82 #endif
83 #ifndef false
84 #define false 0
85 #endif
86 
87 int lo_pattern_match(const char *str, const char *p)
88 {
89  int negate;
90  int match;
91  char c;
92 
93  while (*p) {
94  if (!*str && *p != '*')
95  return false;
96 
97  switch (c = *p++) {
98 
99  case '*':
100  while (*p == '*' && *p != '/')
101  p++;
102 
103  if (!*p)
104  return true;
105 
106 // if (*p != '?' && *p != '[' && *p != '\\')
107  if (*p != '?' && *p != '[' && *p != '{')
108  while (*str && *p != *str)
109  str++;
110 
111  while (*str) {
112  if (lo_pattern_match(str, p))
113  return true;
114  str++;
115  }
116  return false;
117 
118  case '?':
119  if (*str)
120  break;
121  return false;
122  /*
123  * set specification is inclusive, that is [a-z] is a, z and
124  * everything in between. this means [z-a] may be interpreted
125  * as a set that contains z, a and nothing in between.
126  */
127  case '[':
128  if (*p != NEGATE)
129  negate = false;
130  else {
131  negate = true;
132  p++;
133  }
134 
135  match = false;
136 
137  while (!match && (c = *p++)) {
138  if (!*p)
139  return false;
140  if (*p == '-') { /* c-c */
141  if (!*++p)
142  return false;
143  if (*p != ']') {
144  if (*str == c || *str == *p ||
145  (*str > c && *str < *p))
146  match = true;
147  }
148  else { /* c-] */
149  if (*str >= c)
150  match = true;
151  break;
152  }
153  }
154  else { /* cc or c] */
155  if (c == *str)
156  match = true;
157  if (*p != ']') {
158  if (*p == *str)
159  match = true;
160  }
161  else
162  break;
163  }
164  }
165 
166  if (negate == match)
167  return false;
168  /*
169  * if there is a match, skip past the cset and continue on
170  */
171  while (*p && *p != ']')
172  p++;
173  if (!*p++) /* oops! */
174  return false;
175  break;
176 
177  /*
178  * {astring,bstring,cstring}
179  */
180  case '{':
181  {
182  // *p is now first character in the {brace list}
183  const char *place = str; // to backtrack
184  const char *remainder = p; // to forwardtrack
185 
186  // find the end of the brace list
187  while (*remainder && *remainder != '}')
188  remainder++;
189  if (!*remainder++) /* oops! */
190  return false;
191 
192  c = *p++;
193 
194  while (c) {
195  if (c == ',') {
196  if(lo_pattern_match(str, remainder)) {
197  return true;
198  } else {
199  // backtrack on test string
200  str = place;
201  // continue testing,
202  // skip comma
203  if (!*p++) // oops
204  return false;
205  }
206  }
207  else if (c == '}') {
208  // continue normal pattern matching
209  if (!*p && !*str) return true;
210  str--; // str is incremented again below
211  break;
212  } else if (c == *str) {
213  str++;
214  if (!*str && *remainder)
215  return false;
216  } else { // skip to next comma
217  str = place;
218  while (*p != ',' && *p != '}' && *p)
219  p++;
220  if (*p == ',')
221  p++;
222  else if (*p == '}') {
223  return false;
224  }
225  }
226  c = *p++;
227  }
228  }
229 
230  break;
231 
232  /* Not part of OSC pattern matching
233  case '\\':
234  if (*p)
235  c = *p++;
236  */
237 
238  default:
239  if (c != *str)
240  return false;
241  break;
242 
243  }
244  str++;
245  }
246 
247  return !*str;
248 }