libflashprog: Support sub-patterns when matching chip names
Beside looking for matching parenthesis pairs, special care needs to
be taken for parentheses in the searched name when they match paren-
theses in the pattern. For instance when looking at the entry
"MX25L4005(A/C)/MX25L4006E"
searching for
"MX25L4005(A"
shouldn't match because the following '/' is in a sub-pattern. But a
"MX25L4005(A/C)"
should match.
Otherwise, we just apply the pattern matching recursively.
Change-Id: I3439470794ecfe639a97e40ad8dae4912b737582
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/568
Reviewed-by: Jakob Haufe <sur5r@sur5r.net>
diff --git a/libflashprog.c b/libflashprog.c
index 8db9d20..c8b9c5e 100644
--- a/libflashprog.c
+++ b/libflashprog.c
@@ -227,9 +227,25 @@
return ret;
}
+/* Search for a character in string accounting for the parenthesis level. */
+static const char *strlvlchr(const char *s, int paren_level, char c)
+{
+ for (; *s != '\0'; ++s) {
+ if (*s == c && paren_level == 0)
+ return s;
+ if (*s == ')' && paren_level-- == 0)
+ return NULL;
+ if (*s == '(')
+ ++paren_level;
+ }
+
+ return NULL;
+}
+
/*
* Match our database-entry name patterns. The `searched` string can either
- * be an actual chip name or the full pattern (i.e. containing '/' and parents).
+ * be an actual chip name or the full pattern (i.e. containing '/' and paren-
+ * theses).
*
* Our entry patterns can contain alternatives, separated by '/', for instance
* "MX25L4005A/MX25L4006E", optional sub-patterns in parentheses, for instance
@@ -240,30 +256,47 @@
* only moves forward, `searched` can be rewinded when the pattern contains
* alternatives.
*
- * TODO: Sub-patterns are not supported yet.
- *
* Returns the remaining part of `searched` that wasn't matched.
*/
static const char *match_chip_name(const char *pattern, const char *const searched)
{
const char *cur_match, *best_alt_match = searched;
+ int paren_level;
next_alternative:
- for (cur_match = searched; *pattern != '\0'; ++cur_match, ++pattern) {
- if (*pattern == '/') {
+ for (cur_match = searched, paren_level = 0; *pattern != '\0'; ++cur_match, ++pattern) {
+ if (paren_level == 0 && (*pattern == '/' || *pattern == ')')) {
if (*cur_match == '\0') /* exact (sub-)pattern match */
return cur_match;
if (cur_match > best_alt_match) /* cache current, best match */
best_alt_match = cur_match;
+ if (*pattern == ')') /* end of sub-pattern */
+ return best_alt_match;
}
- if (*cur_match == *pattern) /* character match */
+ if (*cur_match == *pattern) { /* character match */
+ if (*pattern == '(')
+ ++paren_level;
+ if (*pattern == ')')
+ --paren_level;
continue;
+ }
if (*cur_match != '\0' && *pattern == '.') /* wildcard match */
continue;
+ if (*pattern == '(') { /* potential sub-pattern */
+ const char *const sub_end = strlvlchr(pattern + 1, 0, ')');
+ if (sub_end) {
+ const char *sub_match = match_chip_name(pattern + 1, cur_match);
+ if (sub_match > cur_match) {
+ cur_match = sub_match - 1;
+ pattern = sub_end;
+ continue;
+ }
+ }
+ }
/* skip current alternative, it didn't match */
- const char *const delim = strchr(pattern, '/');
+ const char *const delim = strlvlchr(pattern, paren_level, '/');
if (delim) {
pattern = delim + 1;
goto next_alternative;