libflashprog: Match chip names to multi-chip database entries

Our database entries that support multiple chips follow specific
patterns:  They can contain alternatives, separated by '/', e.g.

    "MX25L4005A/MX25L4006E"

optional sub-patterns in parentheses, e.g.

    "Am29F002(N)BT"

which matches both "Am29F002BT" and "Am29F002NBT",
and wildcards '.' that match any character.

We compare the entry's pattern and the searched name char-by-char.
The pattern only moves forward.  The searched name can be rewinded
when the pattern contains alternatives.

Ignore sub-patterns for now, to ease review.

Change-Id: I5c938cf898e94cd9119aaafd12fbeb8111b3bc2d
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/567
Reviewed-by: Jakob Haufe <sur5r@sur5r.net>
diff --git a/libflashprog.c b/libflashprog.c
index ef85d06..8db9d20 100644
--- a/libflashprog.c
+++ b/libflashprog.c
@@ -227,12 +227,61 @@
 	return ret;
 }
 
+/*
+ * 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).
+ *
+ * Our entry patterns can contain alternatives, separated by '/', for instance
+ * "MX25L4005A/MX25L4006E", optional sub-patterns in parentheses, for instance
+ * "Am29F002(N)BT" which matches both "Am29F002BT" and "Am29F002NBT", and wild-
+ * cards '.' that match any character.
+ *
+ * We compare the entry's pattern and `searched` char-by-char. The pattern
+ * 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;
+
+next_alternative:
+	for (cur_match = searched; *pattern != '\0'; ++cur_match, ++pattern) {
+		if (*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 (*cur_match == *pattern)			/* character match */
+			continue;
+		if (*cur_match != '\0' && *pattern == '.')	/* wildcard match */
+			continue;
+
+		/* skip current alternative, it didn't match */
+		const char *const delim = strchr(pattern, '/');
+		if (delim) {
+			pattern = delim + 1;
+			goto next_alternative;
+		}
+
+		/* last alternative didn't match, return cached match if any */
+		return best_alt_match;
+	}
+
+	return MAX(cur_match, best_alt_match);
+}
+
 /** @private */
 const struct flashchip *flashprog_chip_by_name(const char *chip_name)
 {
 	const struct flashchip *chip;
-	for (chip = flashchips; chip->name; ++chip) {
-		if (!strcmp(chip->name, chip_name))
+	for (chip = flashchips; *chip_name != '\0' && chip->name; ++chip) {
+		if (*match_chip_name(chip->name, chip_name) == '\0')
 			return chip;
 	}
 	msg_cerr("Error: Unknown chip '%s' specified.\n", chip_name);