[2/2] eal: add fnmatch implementation on Windows

Message ID 20200429232427.7112-3-pallavi.kadam@intel.com (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers
Series Windows logging |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/travis-robot warning Travis build: failed
ci/Intel-compilation success Compilation OK

Commit Message

Kadam, Pallavi April 29, 2020, 11:24 p.m. UTC
  Added fnmatch implementation on Windows to support
log level arguments.
The source file is with BSD-3-Clause license.
https://github.com/lattera/freebsd/blob/master/usr.bin/csup/fnmatch.c

Signed-off-by: Pallavi Kadam <pallavi.kadam@intel.com>
Reviewed-by: Ranjit Menon <ranjit.menon@intel.com>
Reviewed-by: Tasnim Bashar <tbashar@mellanox.com>
Tested-by: Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
---
 lib/librte_eal/windows/fnmatch.c         | 172 +++++++++++++++++++++++
 lib/librte_eal/windows/include/fnmatch.h |  16 +--
 lib/librte_eal/windows/meson.build       |   1 +
 3 files changed, 181 insertions(+), 8 deletions(-)
 create mode 100644 lib/librte_eal/windows/fnmatch.c
  

Comments

Thomas Monjalon April 30, 2020, 6:52 a.m. UTC | #1
30/04/2020 01:24, Pallavi Kadam:
> Added fnmatch implementation on Windows to support
> log level arguments.
> The source file is with BSD-3-Clause license.
> https://github.com/lattera/freebsd/blob/master/usr.bin/csup/fnmatch.c

Sorry for the naive question, I don't know Windows programming.

Do we really need this external code?
Why RtlIsNameInExpression from Windows cannot be used?
https://docs.microsoft.com/en-us/windows/win32/devnotes/rtlisnameinexpression
  
Dmitry Kozlyuk April 30, 2020, 7:30 a.m. UTC | #2
On 2020-04-30 08:52 GMT+0200 Thomas Monjalon wrote:
> 30/04/2020 01:24, Pallavi Kadam:
> > Added fnmatch implementation on Windows to support
> > log level arguments.
> > The source file is with BSD-3-Clause license.
> > https://github.com/lattera/freebsd/blob/master/usr.bin/csup/fnmatch.c  
> 
> Sorry for the naive question, I don't know Windows programming.
> 
> Do we really need this external code?
> Why RtlIsNameInExpression from Windows cannot be used?
> https://docs.microsoft.com/en-us/windows/win32/devnotes/rtlisnameinexpression

The general reason not to use Win32 API for globbing is poorly documented
contract: what are the exact matching rules? They're definitely incompatible
with fnmatch(3). IMO small external code is better than unknown behavior.

RtlIsNameInExpression is an internal call for drivers and services with a
cumbersome API. PathMatchSpecA is the user-mode interface, but see above.

https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-pathmatchspeca
  
Menon, Ranjit May 1, 2020, 1:08 a.m. UTC | #3
On 4/30/2020 12:30 AM, Dmitry Kozlyuk wrote:
> On 2020-04-30 08:52 GMT+0200 Thomas Monjalon wrote:
>> 30/04/2020 01:24, Pallavi Kadam:
>>> Added fnmatch implementation on Windows to support
>>> log level arguments.
>>> The source file is with BSD-3-Clause license.
>>> https://github.com/lattera/freebsd/blob/master/usr.bin/csup/fnmatch.c
>>
>> Sorry for the naive question, I don't know Windows programming.
>>
>> Do we really need this external code?
>> Why RtlIsNameInExpression from Windows cannot be used?
>> https://docs.microsoft.com/en-us/windows/win32/devnotes/rtlisnameinexpression
> 
> The general reason not to use Win32 API for globbing is poorly documented
> contract: what are the exact matching rules? They're definitely incompatible
> with fnmatch(3). IMO small external code is better than unknown behavior.
> 
> RtlIsNameInExpression is an internal call for drivers and services with a
> cumbersome API. PathMatchSpecA is the user-mode interface, but see above.
> 
> https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-pathmatchspeca
> 

I will agree with Dmitry here. The fnmatch external code is shipping, 
production quality code. So it's better to use it than writing something 
new.

ranjit m.
  
Thomas Monjalon May 4, 2020, 4:51 p.m. UTC | #4
01/05/2020 03:08, Ranjit Menon:
> On 4/30/2020 12:30 AM, Dmitry Kozlyuk wrote:
> > On 2020-04-30 08:52 GMT+0200 Thomas Monjalon wrote:
> >> 30/04/2020 01:24, Pallavi Kadam:
> >>> Added fnmatch implementation on Windows to support
> >>> log level arguments.
> >>> The source file is with BSD-3-Clause license.
> >>> https://github.com/lattera/freebsd/blob/master/usr.bin/csup/fnmatch.c
> >>
> >> Sorry for the naive question, I don't know Windows programming.
> >>
> >> Do we really need this external code?
> >> Why RtlIsNameInExpression from Windows cannot be used?
> >> https://docs.microsoft.com/en-us/windows/win32/devnotes/rtlisnameinexpression
> > 
> > The general reason not to use Win32 API for globbing is poorly documented
> > contract: what are the exact matching rules? They're definitely incompatible
> > with fnmatch(3). IMO small external code is better than unknown behavior.
> > 
> > RtlIsNameInExpression is an internal call for drivers and services with a
> > cumbersome API. PathMatchSpecA is the user-mode interface, but see above.
> > 
> > https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-pathmatchspeca
> > 
> 
> I will agree with Dmitry here. The fnmatch external code is shipping, 
> production quality code. So it's better to use it than writing something 
> new.

OK thank you for the explanations and opinions.
  

Patch

diff --git a/lib/librte_eal/windows/fnmatch.c b/lib/librte_eal/windows/fnmatch.c
new file mode 100644
index 000000000..f622bf54c
--- /dev/null
+++ b/lib/librte_eal/windows/fnmatch.c
@@ -0,0 +1,172 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 1989, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)fnmatch.c	8.2 (Berkeley) 4/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
+ * Compares a filename or pathname to a pattern.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "fnmatch.h"
+
+#define EOS	'\0'
+
+static const char *rangematch(const char *, char, int);
+
+int
+fnmatch(const char *pattern, const char *string, int flags)
+{
+	const char *stringstart;
+	char c, test;
+
+	for (stringstart = string;;)
+		switch (c = *pattern++) {
+		case EOS:
+			if ((flags & FNM_LEADING_DIR) && *string == '/')
+				return (0);
+			return (*string == EOS ? 0 : FNM_NOMATCH);
+		case '?':
+			if (*string == EOS)
+				return (FNM_NOMATCH);
+			if (*string == '/' && (flags & FNM_PATHNAME))
+				return (FNM_NOMATCH);
+			if (*string == '.' && (flags & FNM_PERIOD) &&
+			    (string == stringstart ||
+			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+				return (FNM_NOMATCH);
+			++string;
+			break;
+		case '*':
+			c = *pattern;
+			/* Collapse multiple stars. */
+			while (c == '*')
+				c = *++pattern;
+
+			if (*string == '.' && (flags & FNM_PERIOD) &&
+			    (string == stringstart ||
+			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+				return (FNM_NOMATCH);
+
+			/* Optimize for pattern with * at end or before /. */
+			if (c == EOS)
+				if (flags & FNM_PATHNAME)
+					return ((flags & FNM_LEADING_DIR) ||
+					    strchr(string, '/') == NULL ?
+					    0 : FNM_NOMATCH);
+				else
+					return (0);
+			else if (c == '/' && flags & FNM_PATHNAME) {
+				string = strchr(string, '/');
+				if (string == NULL)
+					return (FNM_NOMATCH);
+				break;
+			}
+
+			/* General case, use recursion. */
+			while ((test = *string) != EOS) {
+				if (!fnmatch(pattern, string,
+					flags & ~FNM_PERIOD))
+					return (0);
+				if (test == '/' && flags & FNM_PATHNAME)
+					break;
+				++string;
+			}
+			return (FNM_NOMATCH);
+		case '[':
+			if (*string == EOS)
+				return (FNM_NOMATCH);
+			if (*string == '/' && flags & FNM_PATHNAME)
+				return (FNM_NOMATCH);
+			pattern = rangematch(pattern, *string, flags);
+			if (pattern == NULL)
+				return (FNM_NOMATCH);
+			++string;
+			break;
+		case '\\':
+			if (!(flags & FNM_NOESCAPE)) {
+				c = *pattern++;
+				if (c == EOS) {
+					c = '\\';
+					--pattern;
+				}
+			}
+			/* FALLTHROUGH */
+		default:
+			if (c == *string)
+				;
+			else if ((flags & FNM_CASEFOLD) &&
+				 (tolower((unsigned char)c) ==
+				  tolower((unsigned char)*string)))
+				;
+			else if ((flags & FNM_PREFIX_DIRS) && *string == EOS &&
+			     ((c == '/' && string != stringstart) ||
+			     (string == stringstart+1 && *stringstart == '/')))
+				return (0);
+			else
+				return (FNM_NOMATCH);
+			string++;
+			break;
+		}
+	/* NOTREACHED */
+}
+
+static const char *
+rangematch(const char *pattern, char test, int flags)
+{
+	int negate, ok;
+	char c, c2;
+
+	/*
+	 * A bracket expression starting with an unquoted circumflex
+	 * character produces unspecified results (IEEE 1003.2-1992,
+	 * 3.13.2).  This implementation treats it like '!', for
+	 * consistency with the regular expression syntax.
+	 * J.T. Conklin (conklin@ngai.kaleida.com)
+	 */
+	negate = (*pattern == '!' || *pattern == '^');
+	if (negate)
+		++pattern;
+
+	if (flags & FNM_CASEFOLD)
+		test = tolower((unsigned char)test);
+
+	for (ok = 0; (c = *pattern++) != ']';) {
+		if (c == '\\' && !(flags & FNM_NOESCAPE))
+			c = *pattern++;
+		if (c == EOS)
+			return (NULL);
+
+		if (flags & FNM_CASEFOLD)
+			c = tolower((unsigned char)c);
+
+		c2 = *(pattern + 1);
+		if (*pattern == '-' && c2 != EOS && c2 != ']') {
+			pattern += 2;
+			if (c2 == '\\' && !(flags & FNM_NOESCAPE))
+				c2 = *pattern++;
+			if (c2 == EOS)
+				return (NULL);
+
+			if (flags & FNM_CASEFOLD)
+				c2 = tolower((unsigned char)c2);
+
+			if ((unsigned char)c <= (unsigned char)test &&
+			    (unsigned char)test <= (unsigned char)c2)
+				ok = 1;
+		} else if (c == test)
+			ok = 1;
+	}
+	return (ok == negate ? NULL : pattern);
+}
diff --git a/lib/librte_eal/windows/include/fnmatch.h b/lib/librte_eal/windows/include/fnmatch.h
index d0159f07a..142753c35 100644
--- a/lib/librte_eal/windows/include/fnmatch.h
+++ b/lib/librte_eal/windows/include/fnmatch.h
@@ -18,6 +18,13 @@  extern "C" {
 
 #define FNM_NOMATCH 1
 
+#define FNM_NOESCAPE 0x01
+#define FNM_PATHNAME 0x02
+#define FNM_PERIOD 0x04
+#define FNM_LEADING_DIR 0x08
+#define FNM_CASEFOLD 0x10
+#define FNM_PREFIX_DIRS 0x20
+
 /**
  * This function is used for searhing a given string source
  * with the given regular expression pattern.
@@ -34,14 +41,7 @@  extern "C" {
  * @return
  *	if the pattern is found then return 0 or else FNM_NOMATCH
  */
-static inline int fnmatch(__rte_unused const char *pattern,
-		__rte_unused const char *string,
-		__rte_unused int flags)
-{
-	/* TODO */
-	/* This is a stub, not the expected result */
-	return FNM_NOMATCH;
-}
+int fnmatch(const char *pattern, const char *string, int flags);
 
 #ifdef __cplusplus
 }
diff --git a/lib/librte_eal/windows/meson.build b/lib/librte_eal/windows/meson.build
index e5d1d8336..adfc8b9b7 100644
--- a/lib/librte_eal/windows/meson.build
+++ b/lib/librte_eal/windows/meson.build
@@ -9,5 +9,6 @@  sources += files(
 	'eal_lcore.c',
 	'eal_log.c',
 	'eal_thread.c',
+	'fnmatch.c',
 	'getopt.c',
 )