ReactOS 0.4.17-dev-573-g8315b8c
name.c
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS System Libraries
3 * LICENSE: MIT (https://spdx.org/licenses/MIT)
4 * PURPOSE: RtlIsNameInExpression implementation
5 * COPYRIGHT: Copyright 2026 Carl Bialorucki <carl.bialorucki@reactos.org>
6 */
7
8#include <rtl.h>
9#define NDEBUG
10#include <debug.h>
11
12#define DOS_STAR (L'<')
13#define DOS_QM (L'>')
14#define DOS_DOT (L'"')
15
16#define EXPRESSION_IS_WILDCARD(expr, len) \
17 (len == 1 && expr->Buffer[0] == L'*') \
18 || (len == 3 && expr->Buffer[0] == L'*' && expr->Buffer[1] == L'.' && expr->Buffer[2] == L'*')
19#define IS_LITERAL_OR_DOS_DOT(wchar) \
20 (wchar == DOS_DOT || (wchar != L'*' && wchar != DOS_STAR && wchar != L'?' && wchar != DOS_QM))
21
22/* ****** PRIVATE FUNCTIONS ****** */
23
24static __inline BOOLEAN
25RtlpContainsWildcard(
26 _In_ PWCH Buffer,
27 _In_ ULONG Length)
28{
29 for (ULONG i = 0; i < Length; i++)
30 {
31 WCHAR c = Buffer[i];
32 if (c == L'*' || c == L'?' || c == DOS_STAR || c == DOS_QM || c == DOS_DOT)
33 return TRUE;
34 }
35 return FALSE;
36}
37
38static __inline WCHAR
39RtlpGetExprChar(
40 _In_ PWCH Buffer,
41 _In_ ULONG Position,
42 _In_ ULONG Length,
43 _In_ BOOLEAN IgnoreCase,
44 _In_opt_ PWCH UpcaseTable)
45{
46 WCHAR Result = (Position < Length) ? Buffer[Position] : L'\0';
47 if (IgnoreCase)
48 Result = UpcaseTable ? UpcaseTable[Result] : RtlUpcaseUnicodeChar(Result);
49 return Result;
50}
51
52/* ****** PUBLIC FUNCTIONS ****** */
53
54BOOLEAN
55NTAPI
56RtlIsNameInExpression(
57 _In_ PUNICODE_STRING Expression,
58 _In_ PUNICODE_STRING Name,
59 _In_ BOOLEAN IgnoreCase,
60 _In_opt_ PWCH UpcaseTable
61)
62{
63 ULONG NameLen = Name->Length / sizeof(WCHAR);
64 ULONG ExprLen = Expression->Length / sizeof(WCHAR);
65 ULONG NamePos = 0, ExprPos = 0;
66 ULONG StarExprPos = MAXULONG;
67 ULONG StarNamePos = 0;
68
69 /* For more information about this algorithm, see
70 * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fsa/0b034646-4e23-4874-8488-2adac231ff23
71 */
72
73 /* *** Optimizations *** */
74
75 /* Empty string */
76 if (ExprLen == 0 || NameLen == 0)
77 return (ExprLen == 0 && NameLen == 0);
78
79 /* Wildcard only */
80 if (EXPRESSION_IS_WILDCARD(Expression, ExprLen))
81 return TRUE;
82
83 /* Wildcard + literal with no other wildcards */
84 if (ExprLen >= 2
85 && NameLen >= ExprLen - 1
86 && Expression->Buffer[0] == L'*'
87 && !RtlpContainsWildcard(&Expression->Buffer[1], ExprLen - 1))
88 {
89 ExprPos = 1;
90 NamePos = NameLen - (ExprLen - 1);
91
92 while (ExprPos < ExprLen)
93 {
94 WCHAR NameChar = RtlpGetExprChar(Name->Buffer, NamePos, NameLen, IgnoreCase, UpcaseTable);
95 WCHAR ExprChar = RtlpGetExprChar(Expression->Buffer, ExprPos, ExprLen, IgnoreCase, UpcaseTable);
96 if (ExprChar != NameChar)
97 return FALSE;
98 NamePos++;
99 ExprPos++;
100 }
101
102 return TRUE;
103 }
104
105 /* *** Main parser *** */
106
107 for (;;)
108 {
109 /* We passed everything and reached the end of both the name and expression. */
110 if (NamePos >= NameLen && ExprPos >= ExprLen)
111 return TRUE;
112
113 WCHAR NameChar = RtlpGetExprChar(Name->Buffer, NamePos, NameLen, IgnoreCase, UpcaseTable);
114 WCHAR ExprChar = RtlpGetExprChar(Expression->Buffer, ExprPos, ExprLen, IgnoreCase, UpcaseTable);
115
116 /* '?' or character literal */
117 if ((ExprChar == L'?' && NamePos < NameLen)
118 || (IS_LITERAL_OR_DOS_DOT(ExprChar) && ExprChar == NameChar))
119 {
120 NamePos++;
121 ExprPos++;
122 continue;
123 }
124
125 /* '*' */
126 if (ExprChar == L'*')
127 {
128 StarExprPos = ExprPos;
129 StarNamePos = NamePos;
130 ExprPos++;
131 continue;
132 }
133
134 /* DOS_STAR */
135 if (ExprChar == DOS_STAR)
136 {
137 ULONG LookPos = ExprPos + 1;
138 StarExprPos = ExprPos;
139 StarNamePos = NamePos;
140
141 while (LookPos < ExprLen)
142 {
143 WCHAR LookChar = Expression->Buffer[LookPos];
144 if (IS_LITERAL_OR_DOS_DOT(LookChar))
145 break;
146 LookPos++;
147 }
148
149 ExprPos++;
150 continue;
151 }
152
153 /* DOS_QM */
154 if (ExprChar == DOS_QM)
155 {
156 if (NamePos >= NameLen || NameChar == L'.')
157 {
158 /* Dot or end-of-name: skip all consecutive DOS_QM tokens */
159 while (ExprPos < ExprLen && Expression->Buffer[ExprPos] == DOS_QM)
160 ExprPos++;
161 continue;
162 }
163
164 /* Normal DOS_QM: consume one non-dot character */
165 NamePos++;
166 ExprPos++;
167 continue;
168 }
169
170 /* DOS_DOT */
171 if (ExprChar == DOS_DOT)
172 {
173 if (NamePos >= NameLen)
174 {
175 ExprPos++;
176 continue;
177 }
178
179 if (NameChar == L'.')
180 {
181 NamePos++;
182 ExprPos++;
183 continue;
184 }
185 }
186
187 /* Backtrack from a '*' or DOS_STAR */
188 if (StarExprPos != MAXULONG)
189 {
190 /* A DOS_STAR at the end cannot consume a trailing '.' in the name */
191 if (Expression->Buffer[StarExprPos] == DOS_STAR && ExprPos >= ExprLen)
192 {
193 ULONG i;
194 for (i = StarNamePos; i < NameLen; i++)
195 {
196 if (Name->Buffer[i] == L'.')
197 return FALSE;
198 }
199 }
200
201 if (StarNamePos < NameLen)
202 {
203 StarNamePos++;
204 NamePos = StarNamePos;
205 ExprPos = StarExprPos + 1;
206 continue;
207 }
208 }
209
210 return FALSE;
211 }
212}