From: Vibhav Pant vibhavp@gmail.com
--- dlls/cfgmgr32/main.c | 123 +++++++++++++++--- dlls/cfgmgr32/tests/cfgmgr32.c | 4 +- .../tests/devices.c | 4 +- 3 files changed, 107 insertions(+), 24 deletions(-)
diff --git a/dlls/cfgmgr32/main.c b/dlls/cfgmgr32/main.c index 69004ea4b5c..689a2217ad4 100644 --- a/dlls/cfgmgr32/main.c +++ b/dlls/cfgmgr32/main.c @@ -17,6 +17,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <assert.h> + #include "wine/debug.h" #include "wine/rbtree.h" #include "winreg.h" @@ -414,6 +416,9 @@ static HRESULT devprop_filter_eval_compare( const DEV_OBJECT *obj, const DEVPROP cmp = op & DEVPROP_OPERATOR_MODIFIER_IGNORE_CASE ? wcsicmp( prop->Buffer, cmp_prop->Buffer ) : wcscmp( prop->Buffer, cmp_prop->Buffer ); break; + case DEVPROP_TYPE_GUID: + /* Any other comparison operator other than DEVPROP_OPERATOR_EQUALS with GUIDs evaluates to false. */ + if (!(op & DEVPROP_OPERATOR_EQUALS)) break; default: cmp = memcmp( prop->Buffer, cmp_prop->Buffer, prop->BufferSize ); break; @@ -440,45 +445,123 @@ static HRESULT devprop_filter_eval_compare( const DEV_OBJECT *obj, const DEVPROP return ret ? S_OK : S_FALSE; }
-/* Return S_OK if the specified filter expressions match the object, S_FALSE if it doesn't. */ -static HRESULT devprop_filter_matches_object( const DEV_OBJECT *obj, ULONG filters_len, - const DEVPROP_FILTER_EXPRESSION *filters ) +/* Recursively evaluate a sub-expression, returning S_OK on a match or S_FALSE. + * op_outer_logical is the logical operator denoting the outer expression this sub-expression is inside, it should be + * one of the DEVPROP_OPERATOR_*_OPEN operators. + * expr_end_idx gets set to the (relative) index where this sub-expression ends. + */ +static HRESULT devprop_filter_eval_subexpr( const DEV_OBJECT *obj, const DEVPROP_FILTER_EXPRESSION *filters, + const DEVPROP_OPERATOR op_outer_logical, ULONG *expr_end_idx ) { + const DEVPROP_OPERATOR op_outer_close = op_outer_logical + (DEVPROP_OPERATOR_AND_CLOSE - DEVPROP_OPERATOR_AND_OPEN); HRESULT hr = S_OK; ULONG i;
- TRACE( "(%s, %lu, %p)\n", debugstr_DEV_OBJECT( obj ), filters_len, filters ); + TRACE( "(%s, %s, %p)\n", debugstr_DEV_OBJECT( obj ), debugstr_DEVPROP_OPERATOR( op_outer_logical ), filters );
- if (!filters_len) - return S_OK; + assert( op_outer_logical == DEVPROP_OPERATOR_AND_OPEN || op_outer_logical == DEVPROP_OPERATOR_OR_OPEN || + op_outer_logical == DEVPROP_OPERATOR_NOT_OPEN );
- /* By default, the evaluation is performed by AND-ing all individual filter expressions. */ - for (i = 0; i < filters_len; i++) + for (i = 0; filters[i].Operator != op_outer_close; i++) { const DEVPROP_FILTER_EXPRESSION *filter = &filters[i]; - DEVPROP_OPERATOR op = filter->Operator; + DEVPROP_OPERATOR op = filter->Operator, logical;
- if (op == DEVPROP_OPERATOR_NONE) + logical = op & DEVPROP_OPERATOR_MASK_LOGICAL; + if (logical) { - hr = S_FALSE; - break; + DEVPROP_OPERATOR op_close = logical + (DEVPROP_OPERATOR_AND_CLOSE - DEVPROP_OPERATOR_AND_OPEN); + ULONG last_idx = 0; + + if (FAILED(hr = devprop_filter_eval_subexpr( obj, &filters[i + 1], logical, &last_idx ))) return hr; + i += last_idx + 1; + assert( filters[i].Operator == op_close ); } - if (op & (DEVPROP_OPERATOR_MASK_LIST | DEVPROP_OPERATOR_MASK_ARRAY)) + else if (op == DEVPROP_OPERATOR_NONE) hr = S_FALSE; + else if (op & DEVPROP_OPERATOR_MASK_EVAL) { - FIXME( "Unsupported list/array operator: %s\n", debugstr_DEVPROP_OPERATOR( op ) ); - continue; + if (FAILED(hr = devprop_filter_eval_compare( obj, filter ))) goto done; } - if (op & DEVPROP_OPERATOR_MASK_LOGICAL) + else { - FIXME( "Unsupported logical operator: %s\n", debugstr_DEVPROP_OPERATOR( op ) ); + FIXME( "Unsupported operator: %s\n", debugstr_DEVPROP_OPERATOR( op ) ); continue; } - if (op & DEVPROP_OPERATOR_MASK_EVAL) + + + /* See if we can short-circuit. + * Because this function is recursive and the filter expression is validated, the next CLOSE operator will always + * belong to the current expression, marking its end. */ + switch (op_outer_logical) { - hr = devprop_filter_eval_compare( obj, filter ); - if (FAILED( hr ) || hr == S_FALSE) + /* {NOT_OPEN, ..., NOT_CLOSE} is the same as {NOT_OPEN, AND_OPEN, ..., AND_CLOSE, NOT_CLOSE}, so we can + * short circuit here as well. */ + case DEVPROP_OPERATOR_NOT_OPEN: + case DEVPROP_OPERATOR_AND_OPEN: + if (hr == S_FALSE) + { + /* Skip to the end of this sub-expression. */ + while (filters[++i].Operator != op_outer_close) /* nothing */; + goto done; + } + break; + case DEVPROP_OPERATOR_OR_OPEN: + if (hr == S_OK) + { + while (filters[++i].Operator != DEVPROP_OPERATOR_OR_CLOSE) /* nothing */; + goto done; + } break; + DEFAULT_UNREACHABLE; + } + } + +done: + if (SUCCEEDED( hr )) + { + if (expr_end_idx) *expr_end_idx = i; + if (op_outer_logical == DEVPROP_OPERATOR_NOT_OPEN) + hr = (hr == S_OK) ? S_FALSE : S_OK; + } + return hr; +} + +/* Return S_OK if the specified filter expressions match the object, S_FALSE if it doesn't. */ +static HRESULT devprop_filter_matches_object( const DEV_OBJECT *obj, ULONG filters_len, const DEVPROP_FILTER_EXPRESSION *filters ) +{ + HRESULT hr = S_OK; + ULONG i; + + TRACE( "(%s, %lu, %p)\n", debugstr_DEV_OBJECT( obj ), filters_len, filters ); + + if (!filters_len) return S_OK; + + for (i = 0; i < filters_len; i++) + { + const DEVPROP_FILTER_EXPRESSION *filter = &filters[i]; + DEVPROP_OPERATOR op = filter->Operator, logical; + + logical = op & DEVPROP_OPERATOR_MASK_LOGICAL; + if (logical) + { + DEVPROP_OPERATOR op_close = logical + (DEVPROP_OPERATOR_AND_CLOSE - DEVPROP_OPERATOR_AND_OPEN); + ULONG end_idx = 0; + + if (FAILED(hr = devprop_filter_eval_subexpr( obj, &filters[i + 1], logical, &end_idx ))) break; + i += end_idx + 1; + assert( i < filters_len && filters[i].Operator == op_close ); } + else if (op == DEVPROP_OPERATOR_NONE) hr = S_FALSE; + else if (op & DEVPROP_OPERATOR_MASK_EVAL) + hr = devprop_filter_eval_compare( obj, filter ); + else + { + FIXME( "Unsupported operator: %s\n", debugstr_DEVPROP_OPERATOR( op ) ); + continue; + } + + /* By default, the evaluation is performed by AND-ing all individual filter expressions. */ + if (FAILED( hr ) || hr == S_FALSE) break; }
return hr; diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c index 69a9dad8e6e..baefe26746c 100644 --- a/dlls/cfgmgr32/tests/cfgmgr32.c +++ b/dlls/cfgmgr32/tests/cfgmgr32.c @@ -1358,8 +1358,8 @@ static void test_DevGetObjects( void ) hr = pDevGetObjects( DevObjectTypeDeviceInterface, DevQueryFlagNone, 0, NULL, logical_op_test_cases[i].size, logical_op_test_cases[i].expr, &len2, &objects ); ok( hr == S_OK, "got hr %#lx\n", hr ); - todo_wine_if( !len2 ) ok( len2 == len, "got len2 %lu != %lu\n", len2, len ); - todo_wine_if( !len2 ) ok( !!objects, "got objects %p\n", objects ); + ok( len2 == len, "got len2 %lu != %lu\n", len2, len ); + ok( !!objects, "got objects %p\n", objects ); pDevFreeObjects( len2, objects );
winetest_pop_context(); diff --git a/dlls/windows.devices.enumeration/tests/devices.c b/dlls/windows.devices.enumeration/tests/devices.c index 433f9c33f2b..5c7e1fdc0f2 100644 --- a/dlls/windows.devices.enumeration/tests/devices.c +++ b/dlls/windows.devices.enumeration/tests/devices.c @@ -818,7 +818,7 @@ static void test_aqs_filters( void ) test_FindAllAsyncAqsFilter( statics, filters_empty, FALSE, FALSE ); test_CreateWatcherAqsFilter( statics, filters_empty, FALSE, FALSE, FALSE, FALSE );
- test_FindAllAsyncAqsFilter( statics, filters_boolean_op, FALSE, TRUE ); + test_FindAllAsyncAqsFilter( statics, filters_boolean_op, FALSE, FALSE ); test_CreateWatcherAqsFilter( statics, filters_boolean_op, FALSE, FALSE, FALSE, TRUE );
test_FindAllAsyncAqsFilter( statics, filters_simple, FALSE, FALSE ); @@ -827,7 +827,7 @@ static void test_aqs_filters( void ) test_FindAllAsyncAqsFilter( statics, filters_case_insensitive, TRUE, FALSE ); test_CreateWatcherAqsFilter( statics, filters_case_insensitive, TRUE, FALSE, FALSE, FALSE );
- test_FindAllAsyncAqsFilter( statics, filters_precedence, FALSE, TRUE ); + test_FindAllAsyncAqsFilter( statics, filters_precedence, FALSE, FALSE ); test_CreateWatcherAqsFilter( statics, filters_precedence, FALSE, FALSE, FALSE, TRUE );
test_FindAllAsyncAqsFilter( statics, filters_no_results, FALSE, FALSE );