2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/fsrtl/dbcsname.c
5 * PURPOSE: Provides DBCS parsing and other support routines for FSDs
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Pierre Schweitzer (pierre.schweitzer@reactos.org)
10 /* INCLUDES ******************************************************************/
16 /* PUBLIC FUNCTIONS **********************************************************/
19 * @name FsRtlDissectDbcs
22 * Dissects a given path name into first and remaining part.
25 * ANSI string to dissect.
28 * Pointer to user supplied ANSI_STRING, that will later point
29 * to the first part of the original name.
31 * @param RemainingPart
32 * Pointer to user supplied ANSI_STRING, that will later point
33 * to the remaining part of the original name.
38 * Name: \test1\test2\test3
40 * RemainingPart: test2\test3
45 FsRtlDissectDbcs(IN ANSI_STRING Name
,
46 OUT PANSI_STRING FirstPart
,
47 OUT PANSI_STRING RemainingPart
)
49 USHORT FirstPosition
, i
;
50 USHORT SkipFirstSlash
= 0;
53 /* Zero the strings before continuing */
54 RtlZeroMemory(FirstPart
, sizeof(ANSI_STRING
));
55 RtlZeroMemory(RemainingPart
, sizeof(ANSI_STRING
));
57 /* Just quit if the string is empty */
58 if (!Name
.Length
) return;
60 /* Find first backslash */
61 FirstPosition
= Name
.Length
;
62 for (i
= 0; i
< Name
.Length
; i
++)
64 /* First make sure the character it's not the Lead DBCS */
65 if (FsRtlIsLeadDbcsCharacter(Name
.Buffer
[i
]))
69 /* If we found one... */
70 else if (Name
.Buffer
[i
] == '\\')
72 /* If it begins string, just notice it and continue */
79 /* Else, save its position and break out of the loop */
86 /* Set up the first result string */
87 FirstPart
->Buffer
= Name
.Buffer
+ SkipFirstSlash
;
88 FirstPart
->Length
= (FirstPosition
- SkipFirstSlash
);
89 FirstPart
->MaximumLength
= FirstPart
->Length
;
91 /* And second one, if necessary */
92 if (FirstPosition
< (Name
.Length
))
94 RemainingPart
->Buffer
= Name
.Buffer
+ FirstPosition
+ 1;
95 RemainingPart
->Length
= Name
.Length
- (FirstPosition
+ 1);
96 RemainingPart
->MaximumLength
= RemainingPart
->Length
;
101 * @name FsRtlDoesDbcsContainWildCards
104 * Returns TRUE if the given DbcsName contains wildcards such as *, ?,
105 * ANSI_DOS_STAR, ANSI_DOS_DOT, and ANSI_DOS_QM
110 * @return TRUE if there are wildcards, FALSE otherwise
117 FsRtlDoesDbcsContainWildCards(IN PANSI_STRING Name
)
122 /* Check every character */
123 for (i
= 0; i
< Name
->Length
; i
++)
125 /* First make sure it's not the Lead DBCS */
126 if (FsRtlIsLeadDbcsCharacter(Name
->Buffer
[i
]))
130 else if (FsRtlIsAnsiCharacterWild(Name
->Buffer
[i
]))
132 /* Now return if it has a wildcard */
137 /* We didn't return above...so none found */
142 * @name FsRtlIsDbcsInExpression
145 * Check if the Name string is in the Expression string.
148 * The string in which we've to find Name. It can contains wildcards
151 * The string to find. It cannot contain wildcards.
153 * @return TRUE if Name is found in Expression, FALSE otherwise
160 FsRtlIsDbcsInExpression(IN PANSI_STRING Expression
,
161 IN PANSI_STRING Name
)
163 SHORT StarFound
= -1, DosStarFound
= -1;
164 PUSHORT BackTracking
= NULL
, DosBackTracking
= NULL
;
165 USHORT ExpressionPosition
= 0, NamePosition
= 0, MatchingChars
, LastDot
;
168 ASSERT(Name
->Length
);
169 ASSERT(Expression
->Length
);
170 ASSERT(!FsRtlDoesDbcsContainWildCards(Name
));
172 /* Check if we were given strings at all */
173 if (!Name
->Length
|| !Expression
->Length
)
175 /* Return TRUE if both strings are empty, otherwise FALSE */
176 if (Name
->Length
== 0 && Expression
->Length
== 0)
182 /* Check for a shortcut: just one wildcard */
183 if (Expression
->Length
== sizeof(CHAR
))
185 if (Expression
->Buffer
[0] == '*')
189 //ASSERT(FsRtlDoesDbcsContainWildCards(Expression));
191 /* Another shortcut, wildcard followed by some string */
192 if (Expression
->Buffer
[0] == '*')
194 /* Copy Expression to our local variable */
195 ANSI_STRING IntExpression
= *Expression
;
197 /* Skip the first char */
198 IntExpression
.Buffer
++;
199 IntExpression
.Length
-= sizeof(CHAR
);
201 /* Continue only if the rest of the expression does NOT contain
202 any more wildcards */
203 if (!FsRtlDoesDbcsContainWildCards(&IntExpression
))
205 /* Check for a degenerate case */
206 if (Name
->Length
< (Expression
->Length
- sizeof(CHAR
)))
209 /* Calculate position */
210 NamePosition
= (Name
->Length
- IntExpression
.Length
) / sizeof(CHAR
);
212 /* Check whether we are breaking a two chars char (DBCS) */
213 if (NlsMbOemCodePageTag
)
217 while (MatchingChars
< NamePosition
)
219 /* Check if current char is DBCS lead char, if so, jump by two chars */
220 MatchingChars
+= FsRtlIsLeadDbcsCharacter(Name
->Buffer
[MatchingChars
]) ? 2 : 1;
224 if (MatchingChars
> NamePosition
)
229 return RtlEqualMemory(IntExpression
.Buffer
,
230 (Name
->Buffer
+ NamePosition
),
231 IntExpression
.Length
);
235 while (NamePosition
< Name
->Length
&& ExpressionPosition
< Expression
->Length
)
237 /* Basic check to test if chars are equal */
238 if ((Expression
->Buffer
[ExpressionPosition
] == Name
->Buffer
[NamePosition
]))
241 ExpressionPosition
++;
243 /* Check cases that eat one char */
244 else if (Expression
->Buffer
[ExpressionPosition
] == '?')
247 ExpressionPosition
++;
250 else if (Expression
->Buffer
[ExpressionPosition
] == '*')
252 /* Skip contigous stars */
253 while (ExpressionPosition
+ 1 < Expression
->Length
&& Expression
->Buffer
[ExpressionPosition
+ 1] == '*')
255 ExpressionPosition
++;
258 /* Save star position */
261 BackTracking
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
262 Expression
->Length
* sizeof(USHORT
), 'nrSF');
264 BackTracking
[++StarFound
] = ExpressionPosition
++;
266 /* If star is at the end, then eat all rest and leave */
267 if (ExpressionPosition
== Expression
->Length
)
269 NamePosition
= Name
->Length
;
272 /* Allow null matching */
273 else if (Expression
->Buffer
[ExpressionPosition
] != '?' &&
274 Expression
->Buffer
[ExpressionPosition
] != Name
->Buffer
[NamePosition
])
280 else if (Expression
->Buffer
[ExpressionPosition
] == ANSI_DOS_STAR
)
282 /* Skip contigous stars */
283 while (ExpressionPosition
+ 1 < Expression
->Length
&& Expression
->Buffer
[ExpressionPosition
+ 1] == ANSI_DOS_STAR
)
285 ExpressionPosition
++;
288 /* Look for last dot */
290 LastDot
= (USHORT
)-1;
291 while (MatchingChars
< Name
->Length
)
293 if (Name
->Buffer
[MatchingChars
] == '.')
295 LastDot
= MatchingChars
;
296 if (LastDot
> NamePosition
)
303 /* If we don't have dots or we didn't find last yet
304 * start eating everything
306 if (MatchingChars
!= Name
->Length
|| LastDot
== (USHORT
)-1)
308 if (!DosBackTracking
) DosBackTracking
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
309 Expression
->Length
* sizeof(USHORT
), 'nrSF');
310 DosBackTracking
[++DosStarFound
] = ExpressionPosition
++;
312 /* Not the same char, start exploring */
313 if (Expression
->Buffer
[ExpressionPosition
] != Name
->Buffer
[NamePosition
])
318 /* Else, if we are at last dot, eat it - otherwise, null match */
319 if (Name
->Buffer
[NamePosition
] == '.')
322 ExpressionPosition
++;
326 else if (Expression
->Buffer
[ExpressionPosition
] == ANSI_DOS_DOT
)
328 /* We only match dots */
329 if (Name
->Buffer
[NamePosition
] == '.')
333 /* Try to explore later on for null matching */
334 else if (ExpressionPosition
+ 1 < Expression
->Length
&&
335 Name
->Buffer
[NamePosition
] == Expression
->Buffer
[ExpressionPosition
+ 1])
339 ExpressionPosition
++;
342 else if (Expression
->Buffer
[ExpressionPosition
] == ANSI_DOS_QM
)
344 /* We match everything except dots */
345 if (Name
->Buffer
[NamePosition
] != '.')
349 ExpressionPosition
++;
351 /* If nothing match, try to backtrack */
352 else if (StarFound
>= 0)
354 ExpressionPosition
= BackTracking
[StarFound
--];
356 else if (DosStarFound
>= 0)
358 ExpressionPosition
= DosBackTracking
[DosStarFound
--];
360 /* Otherwise, fail */
366 /* Under certain circumstances, expression is over, but name isn't
367 * and we can backtrack, then, backtrack */
368 if (ExpressionPosition
== Expression
->Length
&&
369 NamePosition
!= Name
->Length
&& StarFound
>= 0)
371 ExpressionPosition
= BackTracking
[StarFound
--];
374 /* If we have nullable matching wc at the end of the string, eat them */
375 if (ExpressionPosition
!= Expression
->Length
&& NamePosition
== Name
->Length
)
377 while (ExpressionPosition
< Expression
->Length
)
379 if (Expression
->Buffer
[ExpressionPosition
] != ANSI_DOS_DOT
&&
380 Expression
->Buffer
[ExpressionPosition
] != '*' &&
381 Expression
->Buffer
[ExpressionPosition
] != ANSI_DOS_STAR
)
385 ExpressionPosition
++;
391 ExFreePoolWithTag(BackTracking
, 'nrSF');
395 ExFreePoolWithTag(DosBackTracking
, 'nrSF');
398 return (ExpressionPosition
== Expression
->Length
&& NamePosition
== Name
->Length
);
402 * @name FsRtlIsFatDbcsLegal
405 * Returns TRUE if the given DbcsName is a valid FAT filename (in 8.3)
408 * The filename to check. It can also contains pathname.
410 * @param WildCardsPermissible
411 * If this is set to FALSE and if filename contains wildcard, the function
414 * @param PathNamePermissible
415 * If this is set to FALSE and if the filename comes with a pathname, the
418 * @param LeadingBackslashPermissible
419 * If this is set to FALSE and if the filename starts with a backslash, the
422 * @return TRUE if the DbcsName is legal, FALSE otherwise
429 FsRtlIsFatDbcsLegal(IN ANSI_STRING DbcsName
,
430 IN BOOLEAN WildCardsPermissible
,
431 IN BOOLEAN PathNamePermissible
,
432 IN BOOLEAN LeadingBackslashPermissible
)
434 ANSI_STRING FirstPart
, RemainingPart
;
439 /* Just quit if the string is empty */
440 if (!DbcsName
.Length
)
443 /* Accept special filename if wildcards are allowed */
444 if (WildCardsPermissible
&& (DbcsName
.Length
== 1 || DbcsName
.Length
== 2) && DbcsName
.Buffer
[0] == '.')
446 if (DbcsName
.Length
== 2)
448 if (DbcsName
.Buffer
[1] == '.')
457 /* DbcsName wasn't supposed to be started with \ */
458 if (!LeadingBackslashPermissible
&& DbcsName
.Buffer
[0] == '\\')
460 /* DbcsName was allowed to be started with \, but now, remove it */
461 else if (LeadingBackslashPermissible
&& DbcsName
.Buffer
[0] == '\\')
463 DbcsName
.Buffer
= DbcsName
.Buffer
+ 1;
464 DbcsName
.Length
= DbcsName
.Length
- 1;
465 DbcsName
.MaximumLength
= DbcsName
.MaximumLength
- 1;
468 if (PathNamePermissible
)
470 /* We copy the buffer for FsRtlDissectDbcs call */
471 RemainingPart
.Buffer
= DbcsName
.Buffer
;
472 RemainingPart
.Length
= DbcsName
.Length
;
473 RemainingPart
.MaximumLength
= DbcsName
.MaximumLength
;
475 while (RemainingPart
.Length
> 0)
477 if (RemainingPart
.Buffer
[0] == '\\')
480 /* Call once again our dissect function */
481 FsRtlDissectDbcs(RemainingPart
, &FirstPart
, &RemainingPart
);
483 if (!FsRtlIsFatDbcsLegal(FirstPart
,
484 WildCardsPermissible
,
495 if (WildCardsPermissible
&& FsRtlDoesDbcsContainWildCards(&DbcsName
))
497 for (i
= 0; i
< DbcsName
.Length
; i
++)
499 /* First make sure the character it's not the Lead DBCS */
500 if (FsRtlIsLeadDbcsCharacter(DbcsName
.Buffer
[i
]))
504 /* Then check for bad characters */
505 else if (!FsRtlIsAnsiCharacterLegalFat(DbcsName
.Buffer
[i
], TRUE
))
514 /* Filename must be 8.3 filename */
515 if (DbcsName
.Length
> 12)
518 /* Reset dots count */
521 for (i
= 0; i
< DbcsName
.Length
; i
++)
523 /* First make sure the character it's not the Lead DBCS */
524 if (FsRtlIsLeadDbcsCharacter(DbcsName
.Buffer
[i
]))
526 if (!LastDot
&& (i
>= 7))
529 if (i
== (DbcsName
.Length
- 1))
535 /* Then check for bad characters */
536 else if (!FsRtlIsAnsiCharacterLegalFat(DbcsName
.Buffer
[i
], WildCardsPermissible
))
540 else if (DbcsName
.Buffer
[i
] == '.')
542 /* Filename can only contain one dot */
548 /* We mustn't have spaces before dot or at the end of the filename
549 * and no dot at the beginning of the filename */
550 if (i
== (DbcsName
.Length
- 1) || i
== 0)
553 /* Filename must be 8.3 filename and not 3.8 filename */
554 if ((DbcsName
.Length
- 1) - i
> 3)
557 if ((i
> 0) && DbcsName
.Buffer
[i
- 1] == ' ')
560 /* Filename mustn't finish with a space */
561 else if (DbcsName
.Buffer
[i
] == ' ' && i
== (DbcsName
.Length
- 1))
566 if (!LastDot
&& (i
>= 8))
574 * @name FsRtlIsHpfsDbcsLegal
577 * Returns TRUE if the given DbcsName is a valid HPFS filename
580 * The filename to check. It can also contains pathname.
582 * @param WildCardsPermissible
583 * If this is set to FALSE and if filename contains wildcard, the function
586 * @param PathNamePermissible
587 * If this is set to FALSE and if the filename comes with a pathname, the
590 * @param LeadingBackslashPermissible
591 * If this is set to FALSE and if the filename starts with a backslash, the
594 * @return TRUE if the DbcsName is legal, FALSE otherwise
601 FsRtlIsHpfsDbcsLegal(IN ANSI_STRING DbcsName
,
602 IN BOOLEAN WildCardsPermissible
,
603 IN BOOLEAN PathNamePermissible
,
604 IN BOOLEAN LeadingBackslashPermissible
)
606 ANSI_STRING FirstPart
, RemainingPart
;
610 /* Just quit if the string is empty */
611 if (!DbcsName
.Length
)
614 /* Accept special filename if wildcards are allowed */
615 if (WildCardsPermissible
&& (DbcsName
.Length
== 1 || DbcsName
.Length
== 2) && DbcsName
.Buffer
[0] == '.')
617 if (DbcsName
.Length
== 2)
619 if (DbcsName
.Buffer
[1] == '.')
628 /* DbcsName wasn't supposed to be started with \ */
629 if (!LeadingBackslashPermissible
&& DbcsName
.Buffer
[0] == '\\')
631 /* DbcsName was allowed to be started with \, but now, remove it */
632 else if (LeadingBackslashPermissible
&& DbcsName
.Buffer
[0] == '\\')
634 DbcsName
.Buffer
= DbcsName
.Buffer
+ 1;
635 DbcsName
.Length
= DbcsName
.Length
- 1;
636 DbcsName
.MaximumLength
= DbcsName
.MaximumLength
- 1;
639 if (PathNamePermissible
)
641 /* We copy the buffer for FsRtlDissectDbcs call */
642 RemainingPart
.Buffer
= DbcsName
.Buffer
;
643 RemainingPart
.Length
= DbcsName
.Length
;
644 RemainingPart
.MaximumLength
= DbcsName
.MaximumLength
;
646 while (RemainingPart
.Length
> 0)
648 if (RemainingPart
.Buffer
[0] == '\\')
651 /* Call once again our dissect function */
652 FsRtlDissectDbcs(RemainingPart
, &FirstPart
, &RemainingPart
);
654 if (!FsRtlIsHpfsDbcsLegal(FirstPart
,
655 WildCardsPermissible
,
666 if (DbcsName
.Length
> 255)
669 for (i
= 0; i
< DbcsName
.Length
; i
++)
671 /* First make sure the character it's not the Lead DBCS */
672 if (FsRtlIsLeadDbcsCharacter(DbcsName
.Buffer
[i
]))
674 if (i
== (DbcsName
.Length
- 1))
678 /* Then check for bad characters */
679 else if (!FsRtlIsAnsiCharacterLegalHpfs(DbcsName
.Buffer
[i
], WildCardsPermissible
))
683 /* Filename mustn't finish with a space or a dot */
684 else if ((DbcsName
.Buffer
[i
] == ' ' || DbcsName
.Buffer
[i
] == '.') && i
== (DbcsName
.Length
- 1))