Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b

Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/fmt/regex.c b/src/fmt/regex.c
new file mode 100644
index 0000000..1983fad
--- /dev/null
+++ b/src/fmt/regex.c
@@ -0,0 +1,258 @@
+/**
+ * @file regex.c Implements basic regular expressions
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <ctype.h>
+#include <re_types.h>
+#include <re_fmt.h>
+
+
+/** Defines a character range */
+struct chr {
+	uint8_t min;  /**< Minimum value */
+	uint8_t max;  /**< Maximum value */
+};
+
+
+static bool expr_match(const struct chr *chrv, uint32_t n, uint8_t c,
+		       bool neg)
+{
+	uint32_t i;
+
+	for (i=0; i<n; i++) {
+
+		if (c < chrv[i].min)
+			continue;
+
+		if (c > chrv[i].max)
+			continue;
+
+		break;
+	}
+
+	return neg ? (i == n) : (i != n);
+}
+
+
+/**
+ * Parse a string using basic regular expressions. Any number of matching
+ * expressions can be given, and each match will be stored in a "struct pl"
+ * pointer-length type.
+ *
+ * @param ptr  String to parse
+ * @param len  Length of string
+ * @param expr Regular expressions string
+ *
+ * @return 0 if success, otherwise errorcode
+ *
+ * Example:
+ *
+ *   We parse the buffer for any numerical values, to get a match we must have
+ *   1 or more occurences of the digits 0-9. The result is stored in 'num',
+ *   which is of pointer-length type and will point to the first location in
+ *   the buffer that contains "42".
+ *
+ * <pre>
+ const char buf[] = "foo 42 bar";
+ struct pl num;
+ int err = re_regex(buf, strlen(buf), "[0-9]+", &num);
+
+ here num contains a pointer to '42'
+ * </pre>
+ */
+int re_regex(const char *ptr, size_t len, const char *expr, ...)
+{
+	struct chr chrv[64];
+	const char *p, *ep;
+	bool fm, range = false, ec = false, neg = false, qesc = false;
+	uint32_t n = 0;
+	va_list ap;
+	bool eesc;
+	size_t l;
+
+	if (!ptr || !expr)
+		return EINVAL;
+
+ again:
+	eesc = false;
+	fm = false;
+	l  = len--;
+	p  = ptr++;
+	ep = expr;
+
+	va_start(ap, expr);
+
+	if (!l)
+		goto out;
+
+	for (; *ep; ep++) {
+
+		if ('\\' == *ep && !eesc) {
+			eesc = true;
+			continue;
+		}
+
+		if (!fm) {
+
+			/* Start of character class */
+			if ('[' == *ep && !eesc) {
+				n     = 0;
+				fm    = true;
+				ec    = false;
+				neg   = false;
+				range = false;
+				qesc  = false;
+				continue;
+			}
+
+			if (!l)
+				break;
+
+			if (tolower(*ep) != tolower(*p)) {
+				va_end(ap);
+				goto again;
+			}
+
+			eesc = false;
+			++p;
+			--l;
+			continue;
+		}
+		/* End of character class */
+		else if (ec) {
+
+			uint32_t nm, nmin, nmax;
+			struct pl lpl, *pl = va_arg(ap, struct pl *);
+			bool quote = false, esc = false;
+
+			/* Match 0 or more times */
+			if ('*' == *ep) {
+				nmin = 0;
+				nmax = -1;
+			}
+			/* Match 1 or more times */
+			else if ('+' == *ep) {
+				nmin = 1;
+				nmax = -1;
+			}
+			/* Match exactly n times */
+			else if ('1' <= *ep && *ep <= '9') {
+				nmin = *ep - '0';
+				nmax = *ep - '0';
+			}
+			else
+				break;
+
+			fm = false;
+
+			lpl.p = p;
+			lpl.l = 0;
+
+			for (nm = 0; l && nm < nmax; nm++, p++, l--, lpl.l++) {
+
+				if (qesc) {
+
+					if (esc) {
+						esc = false;
+						continue;
+					}
+
+					switch (*p) {
+
+					case '\\':
+						esc = true;
+						continue;
+
+					case '"':
+						quote = !quote;
+						continue;
+					}
+
+					if (quote)
+						continue;
+				}
+
+				if (!expr_match(chrv, n, tolower(*p), neg))
+					break;
+			}
+
+			/* Strip quotes */
+			if (qesc && lpl.l > 1 &&
+			    lpl.p[0] == '"' && lpl.p[lpl.l - 1] == '"') {
+
+				lpl.p += 1;
+				lpl.l -= 2;
+				nm    -= 2;
+			}
+
+			if ((nm < nmin) || (nm > nmax)) {
+				va_end(ap);
+				goto again;
+			}
+
+			if (pl)
+				*pl = lpl;
+
+			eesc = false;
+			continue;
+		}
+
+		if (eesc) {
+			eesc = false;
+			goto chr;
+		}
+
+		switch (*ep) {
+
+			/* End of character class */
+		case ']':
+			ec = true;
+			continue;
+
+			/* Negate with quote escape */
+		case '~':
+			if (n)
+				break;
+
+			qesc = true;
+			neg  = true;
+			continue;
+
+			/* Negate */
+		case '^':
+			if (n)
+				break;
+
+			neg = true;
+			continue;
+
+			/* Range */
+		case '-':
+			if (!n || range)
+				break;
+
+			range = true;
+			--n;
+			continue;
+		}
+
+	chr:
+		chrv[n].max = tolower(*ep);
+
+		if (range)
+			range = false;
+		else
+			chrv[n].min = tolower(*ep);
+
+		if (++n > ARRAY_SIZE(chrv))
+			break;
+	}
+ out:
+	va_end(ap);
+
+	if (fm)
+		return EINVAL;
+
+	return *ep ? ENOENT : 0;
+}