Skip to content

Commit a98b66d

Browse files
committed
First draft of wildcard functions. For issue #58.
1 parent 42ba197 commit a98b66d

File tree

6 files changed

+630
-0
lines changed

6 files changed

+630
-0
lines changed

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ add_library(shellanything STATIC
7575
ErrorManager.cpp
7676
PropertyManager.h
7777
PropertyManager.cpp
78+
Wildcard.cpp
79+
Wildcard.h
7880
)
7981

8082
add_library(shellext SHARED

src/Wildcard.cpp

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
/**********************************************************************************
2+
* MIT License
3+
*
4+
* Copyright (c) 2018 Antoine Beauchamp
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*********************************************************************************/
24+
25+
//#define _CRTDBG_MAP_ALLOC
26+
//#include <stdlib.h>
27+
//#include <crtdbg.h>
28+
29+
#include "Wildcard.h"
30+
31+
namespace shellanything
32+
{
33+
34+
bool IsWildcard(char c)
35+
{
36+
if (c == '*' || c == '?')
37+
return true;
38+
return false;
39+
}
40+
41+
int getNextWildcardPosition(const IntList & iPositions, int iSourceWildcardPosition)
42+
{
43+
for(unsigned int i=0; i<iPositions.size(); i++)
44+
{
45+
const int & pos = iPositions[i];
46+
if (pos == iSourceWildcardPosition)
47+
{
48+
if (i+1 < iPositions.size())
49+
{
50+
return iPositions[i+1];
51+
}
52+
return -1; //requested wildcard is the last one
53+
}
54+
}
55+
return -1; //wildcard position not found
56+
}
57+
58+
void FindWildcardCharacters(const char * iString, int iOffset, IntList & oIndexes)
59+
{
60+
oIndexes.clear();
61+
int len = (int)std::string(iString).size();
62+
for(int i=iOffset; i<len; i++)
63+
if ( IsWildcard(iString[i]) )
64+
oIndexes.push_back(i);
65+
}
66+
67+
bool HasWildcardCharacters(const char * iValue)
68+
{
69+
IntList positions;
70+
FindWildcardCharacters(iValue, 0, positions);
71+
return positions.size() > 0;
72+
}
73+
74+
bool WildcardSolve( const char * iWildcard,
75+
const char * iValue,
76+
const IntList & iWildcardPositions,
77+
const int iIndexWild,
78+
const int iIndexValue,
79+
WildcardList & oList )
80+
{
81+
int indexWild = iIndexWild;
82+
int indexValue = iIndexValue;
83+
int wildLen = (int)std::string(iWildcard).size();
84+
int valueLen = (int)std::string(iValue).size();
85+
86+
//while wildcard and value not fully solved
87+
while (indexWild < wildLen || indexValue < valueLen)
88+
{
89+
const char & cWild = iWildcard[indexWild];
90+
const char & cValue = iValue[indexValue];
91+
if ( !IsWildcard(cWild) )
92+
{
93+
if (cWild != cValue)
94+
return false; //characters don't match!
95+
96+
//next char
97+
indexWild++;
98+
indexValue++;
99+
}
100+
else
101+
{
102+
//cWild is wildcard
103+
if (cWild == '?')
104+
{
105+
//save wildcard resolve information
106+
WILDCARD w;
107+
w.character = cWild;
108+
w.index = indexWild;
109+
w.value = cValue;
110+
oList.push_back(w);
111+
112+
//next char
113+
indexWild++;
114+
indexValue++;
115+
}
116+
else if (cWild == '*')
117+
{
118+
//if wildcard character * is the last one
119+
if (indexWild+1 >= wildLen)
120+
{
121+
//automatically match the end of the string
122+
123+
//save wildcard resolve information
124+
WILDCARD w;
125+
w.character = cWild;
126+
w.index = indexWild;
127+
w.value = &cValue;
128+
oList.push_back(w);
129+
130+
//next char
131+
indexWild++;
132+
indexValue += (int)w.value.size();
133+
}
134+
else
135+
{
136+
//wildcard character * is not the last one
137+
138+
//compute all possibilities that can fit in * and then use recursion to check if it can be resolved
139+
//compute the possibilities in the from the longest to the shortest ("") for optimizing the replacement string
140+
//since all wildcard caracters must be mapped, do not compute possibilities beyond the next wildcard character
141+
142+
143+
//compute next character
144+
const char & nextWild = (&cWild)[1];
145+
146+
int nextWildcardPosition = getNextWildcardPosition(iWildcardPositions, indexWild);
147+
148+
//compute fixed characters between this wildcard and the last one (of the end of the file)
149+
//ie: fgh in abc*fgh?j or abc*fgh
150+
std::string fixedCharacters = &nextWild;
151+
if (nextWildcardPosition != -1)
152+
{
153+
//there is at least another wildcard character after this one
154+
fixedCharacters = &nextWild;
155+
size_t size = nextWildcardPosition - indexWild;
156+
fixedCharacters.resize(size);
157+
}
158+
159+
//compute maximum length of replacementString
160+
int remainingCharactersInValue = valueLen-indexValue;
161+
int replacementStringMaxLength = remainingCharactersInValue-(int)fixedCharacters.size();
162+
163+
//compute all possibilities that can fit in * with a length in [0,replacementStringMaxLength]
164+
for(int length=replacementStringMaxLength; length>=0; length--)
165+
{
166+
//assuming replacement string is the right one
167+
168+
std::string replacementString = &cValue;
169+
replacementString.resize(length);
170+
171+
WildcardList tmpList = oList;
172+
173+
//save wildcard resolve information
174+
WILDCARD w;
175+
w.character = cWild;
176+
w.index = indexWild;
177+
w.value = replacementString;
178+
tmpList.push_back(w);
179+
180+
//next char
181+
int tmpIndexWild = indexWild+1;
182+
int tmpIndexValue = indexValue + (int)w.value.size();
183+
184+
//execute recursive call
185+
bool solved = WildcardSolve(iWildcard, iValue, iWildcardPositions, tmpIndexWild, tmpIndexValue, tmpList);
186+
if (solved)
187+
{
188+
//solved!
189+
//refresh oList
190+
oList = tmpList;
191+
return true;
192+
}
193+
}
194+
195+
//all possibilities were checked
196+
//unable to solve * wildcard
197+
return false;
198+
}
199+
}
200+
}
201+
}
202+
203+
if (indexWild == wildLen && indexValue == valueLen)
204+
return true; //solved
205+
if ((indexWild == wildLen && indexValue < valueLen) ||
206+
(indexWild == wildLen && indexValue < valueLen) )
207+
return false; //reached the end of wildcard or the end of value
208+
return false; //???
209+
}
210+
211+
bool WildcardSolve(const char * iWildcard, const char * iValue, WildcardList & oList)
212+
{
213+
oList.clear();
214+
int indexWild = 0;
215+
int indexValue = 0;
216+
217+
//find all wildcard character positions
218+
IntList wildcardPositions;
219+
FindWildcardCharacters(iWildcard, 0, wildcardPositions);
220+
221+
//if no wildcard character, strings must be equal
222+
if (wildcardPositions.size() == 0)
223+
return (std::string(iWildcard) == std::string(iValue));
224+
225+
//solve wildcards
226+
return WildcardSolve(iWildcard, iValue, wildcardPositions, indexWild, indexValue, oList);
227+
}
228+
229+
bool WildcardMatch(const char * iWildcard, const char * iValue)
230+
{
231+
WildcardList dummy;
232+
return WildcardSolve(iWildcard, iValue, dummy);
233+
}
234+
235+
WildcardGroupList FindWildcardGroups(const char * iString)
236+
{
237+
WildcardGroupList groups;
238+
239+
IntList positions;
240+
FindWildcardCharacters(iString, 0, positions);
241+
for(size_t i=0; i<positions.size(); i++)
242+
{
243+
int & pos = positions[i];
244+
char c = iString[pos];
245+
246+
//add current wildcard type to list
247+
bool found = false;
248+
for(size_t j=0; j<groups.size(); j++)
249+
{
250+
if (groups[j].character == c)
251+
{
252+
groups[j].indexes.push_back(pos);
253+
found = true;
254+
}
255+
}
256+
if (!found)
257+
{
258+
//create a new one
259+
WILDCARD_GROUP g;
260+
g.character = c;
261+
g.indexes.push_back(pos);
262+
groups.push_back(g);
263+
}
264+
}
265+
266+
return groups;
267+
}
268+
269+
} //namespace shellanything

src/Wildcard.h

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**********************************************************************************
2+
* MIT License
3+
*
4+
* Copyright (c) 2018 Antoine Beauchamp
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*********************************************************************************/
24+
25+
#ifndef SA_WILDCARD_H
26+
#define SA_WILDCARD_H
27+
28+
#include <string>
29+
#include <vector>
30+
31+
namespace shellanything
32+
{
33+
34+
/// <summary>
35+
/// Defines a wildcard expansion solution to match a given string.
36+
/// </summary>
37+
struct WILDCARD
38+
{
39+
///<summary>The wildcard character found in the given string.</summary>
40+
char character;
41+
42+
///<summary>The index at which the wildcard character was found.</summary>
43+
int index;
44+
45+
///<summary>The expanded value of the wildcard to match the given string.</summary>
46+
std::string value;
47+
};
48+
49+
/// <summary>
50+
/// Defines a list of WILDCARD
51+
/// </summary>
52+
typedef std::vector<WILDCARD> WildcardList;
53+
54+
typedef std::vector<int> IntList;
55+
56+
struct WILDCARD_GROUP
57+
{
58+
char character;
59+
IntList indexes;
60+
};
61+
typedef std::vector<WILDCARD_GROUP> WildcardGroupList;
62+
63+
/// <summary>
64+
/// Evaluates if the given character is a wildcard character supported by the library.
65+
/// </summary>
66+
/// <param name="c">The character to evaluate.</param>
67+
/// <returns>Returns true if the given character is a wildcard character. Returns false otherwise.</returns>
68+
bool IsWildcard(char c);
69+
70+
/// <summary>
71+
/// Evaluates if the given string contains at least one wildcard character.
72+
/// </summary>
73+
/// <param name="c">The string to evaluate.</param>
74+
/// <returns>Returns true when the given string contains at least one wildcard character. Returns false otherwise.</returns>
75+
bool HasWildcardCharacters(const char * iValue);
76+
77+
/// <summary>
78+
/// Returns the position of each wildcard character into oIndexes from iString starting at iOffset.
79+
/// </summary>
80+
/// <param name="iString">The string to search.</param>
81+
/// <param name="iOffset">The index in the string where to start the search from.</param>
82+
/// <param name="oIndexes">The offsets where wildcard characters was found.</param>
83+
void FindWildcardCharacters(const char * iString, int iOffset, IntList & oIndexes);
84+
85+
//Processes iWildcard and finds expanding parameters in order to convert iWildcard to iValue
86+
bool WildcardSolve(const char * iWildcard, const char * iValue, WildcardList & oList);
87+
88+
//Returns true if iWildcard can be expanded to match iValue.
89+
bool WildcardMatch(const char * iWildcard, const char * iValue);
90+
91+
//Returns all wildcard characters and index from iString groupped by wildcard character
92+
WildcardGroupList FindWildcardGroups(const char * iString);
93+
94+
} //namespace shellanything
95+
96+
#endif //SA_ACTION_PROPERTY_H

test/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ add_executable(shellanything_unittest
8181
TestUnicode.h
8282
TestValidator.cpp
8383
TestValidator.h
84+
TestWildcard.h
85+
TestWildcard.cpp
8486
TestWin32Registry.cpp
8587
TestWin32Registry.h
8688
TestWin32Utils.cpp

0 commit comments

Comments
 (0)