xref: /reactos/sdk/lib/rtl/unicodeprefix.c (revision c2c66aff)
1 /*
2  * COPYRIGHT:         See COPYING in the top level directory
3  * PROJECT:           ReactOS system libraries
4  * PURPOSE:           Unicode Prefix implementation
5  * FILE:              lib/rtl/unicodeprefix.c
6  * PROGRAMMER:        Alex Ionescu (alex@relsoft.net)
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include <rtl.h>
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 /*
17  * FIXME: Try to find the official names and add to NDK
18  * Definitions come from fastfat driver.
19  */
20 typedef USHORT NODE_TYPE_CODE;
21 #define PFX_NTC_TABLE       ((NODE_TYPE_CODE)0x0800)
22 #define PFX_NTC_ROOT        ((NODE_TYPE_CODE)0x0801)
23 #define PFX_NTC_CHILD       ((NODE_TYPE_CODE)0x0802)
24 #define PFX_NTC_CASE_MATCH  ((NODE_TYPE_CODE)0x0803)
25 
26 /* FUNCTIONS ***************************************************************/
27 
28 ULONG
29 NTAPI
30 ComputeUnicodeNameLength(IN PUNICODE_STRING UnicodeName)
31 {
32     ULONG Chars = UnicodeName->Length / sizeof(WCHAR);
33     ULONG i, NamesFound = 1;
34 
35     /* Loop the string */
36     for (i = 0; i < (Chars - 1); i++)
37     {
38         /* Check if we found a backslash, meaning another name */
39         if (UnicodeName->Buffer[i] == '\\') NamesFound++;
40     }
41 
42     /* Return the number of names found */
43     return NamesFound;
44 }
45 
46 RTL_GENERIC_COMPARE_RESULTS
47 NTAPI
48 CompareUnicodeStrings(IN PUNICODE_STRING Prefix,
49                       IN PUNICODE_STRING String,
50                       IN ULONG CaseCheckChar)
51 {
52     ULONG StringLength = String->Length / sizeof(WCHAR);
53     ULONG PrefixLength = Prefix->Length / sizeof(WCHAR);
54     ULONG ScanLength = min(StringLength, PrefixLength);
55     ULONG i;
56     WCHAR FoundPrefix, FoundString;
57     PWCHAR p, p1;
58 
59     /* Handle case noticed in npfs when Prefix = '\' and name starts with '\' */
60     if ((PrefixLength == 1) &&
61         (Prefix->Buffer[0] == '\\') &&
62         (StringLength > 1) &&
63         (String->Buffer[0] == '\\'))
64     {
65         /* The string is actually a prefix */
66         return -1;
67     }
68 
69     /* Validate the Case Check Character Position */
70     if (CaseCheckChar > ScanLength) CaseCheckChar = ScanLength;
71 
72     /* Do the case sensitive comparison first */
73     for (i = 0; i < CaseCheckChar; i++)
74     {
75         /* Compare the two characters */
76         if (Prefix->Buffer[i] != String->Buffer[i]) break;
77     }
78 
79     /* Save the characters we found */
80     FoundPrefix = Prefix->Buffer[i];
81     FoundString = String->Buffer[i];
82 
83     /* Check if we exausted the search above */
84     if (i == CaseCheckChar)
85     {
86         /* Do a case-insensitive search */
87         p = &Prefix->Buffer[i];
88         p1 = &String->Buffer[i];
89         do
90         {
91             /* Move to the next character */
92             FoundPrefix = *p++;
93             FoundString = *p1++;
94 
95             /* Compare it */
96             if (FoundPrefix != FoundString)
97             {
98                 /* Upcase the characters */
99                 FoundPrefix = RtlpUpcaseUnicodeChar(FoundPrefix);
100                 FoundString = RtlpUpcaseUnicodeChar(FoundString);
101 
102                 /* Compare them again */
103                 if (FoundPrefix != FoundString) break;
104             }
105 
106             /* Move to the next char */
107             i++;
108         } while (i < ScanLength);
109     }
110 
111     /* Check if we weren't able to find a match in the loops */
112     if (i < ScanLength)
113     {
114         /* If the prefix character found was a backslash, this is a less */
115         if (FoundPrefix == '\\') return GenericLessThan;
116 
117         /* If the string character found was a backslack, then this is a more */
118         if (FoundString == '\\') return GenericGreaterThan;
119 
120         /* None of those two special cases, do a normal check */
121         if (FoundPrefix < FoundString) return GenericLessThan;
122 
123         /* The only choice left is that Prefix > String */
124         return GenericGreaterThan;
125     }
126 
127     /* If we got here, a match was found. Check if the prefix is smaller */
128     if (PrefixLength < StringLength)
129     {
130         /* Check if the string is actually a prefix */
131         if (String->Buffer[PrefixLength] == '\\') return -1;
132 
133         /* It's not a prefix, and it's shorter, so it's a less */
134         return GenericLessThan;
135     }
136 
137     /* Check if the prefix is longer */
138     if (PrefixLength > StringLength) return GenericGreaterThan;
139 
140     /* If we got here, then they are 100% equal */
141     return GenericEqual;
142 }
143 
144 /*
145  * @implemented
146  */
147 PUNICODE_PREFIX_TABLE_ENTRY
148 NTAPI
149 RtlFindUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable,
150                      PUNICODE_STRING FullName,
151                      ULONG CaseInsensitiveIndex)
152 {
153     ULONG NameCount;
154     PUNICODE_PREFIX_TABLE_ENTRY CurrentEntry, PreviousEntry, Entry, NextEntry;
155     PRTL_SPLAY_LINKS SplayLinks;
156     RTL_GENERIC_COMPARE_RESULTS Result;
157 
158     DPRINT("RtlFindUnicodePrefix(): Table %p, FullName %wZ, "
159         "CaseInsensitive %lu\n", PrefixTable, FullName, CaseInsensitiveIndex);
160 
161     /* Find out how many names there are */
162     NameCount = ComputeUnicodeNameLength(FullName);
163 
164     /* Find the right spot where to start looking for this entry */
165     PreviousEntry = (PUNICODE_PREFIX_TABLE_ENTRY)PrefixTable;
166     CurrentEntry = PreviousEntry->NextPrefixTree;
167     while (CurrentEntry->NameLength > (CSHORT)NameCount)
168     {
169         /* Not a match, move to the next entry */
170         PreviousEntry = CurrentEntry;
171         CurrentEntry = CurrentEntry->NextPrefixTree;
172     }
173 
174     /* Loop every entry which has valid entries */
175     while (CurrentEntry->NameLength)
176     {
177         /* Get the splay links and loop */
178         SplayLinks = &CurrentEntry->Links;
179         while (SplayLinks)
180         {
181             /* Get the entry */
182             Entry = CONTAINING_RECORD(SplayLinks,
183                                       UNICODE_PREFIX_TABLE_ENTRY,
184                                       Links);
185 
186             /* Do the comparison */
187             Result = CompareUnicodeStrings(Entry->Prefix, FullName, 0);
188             if (Result == GenericGreaterThan)
189             {
190                 /* Prefix is greater, so restart on the left child */
191                 SplayLinks = RtlLeftChild(SplayLinks);
192                 continue;
193             }
194             else if (Result == GenericLessThan)
195             {
196                 /* Prefix is smaller, so restart on the right child */
197                 SplayLinks = RtlRightChild(SplayLinks);
198                 continue;
199             }
200 
201             /*
202              * We have a match, check if this was a case-sensitive search
203              * NOTE: An index of 0 means case-insensitive(ie, we'll be case
204              * insensitive since index 0, ie, all the time)
205              */
206             if (!CaseInsensitiveIndex)
207             {
208                 /*
209                  * Check if this entry was a child. We need to return the root,
210                  * so if this entry was a child, we'll splay the tree and get
211                  * the root, and set the current entry as a child.
212                  */
213                 if (Entry->NodeTypeCode == PFX_NTC_CHILD)
214                 {
215                     /* Get the next entry */
216                     NextEntry = CurrentEntry->NextPrefixTree;
217 
218                     /* Make the current entry become a child */
219                     CurrentEntry->NodeTypeCode = PFX_NTC_CHILD;
220                     CurrentEntry->NextPrefixTree = NULL;
221 
222                     /* Splay the tree */
223                     SplayLinks = RtlSplay(&Entry->Links);
224 
225                     /* Get the new root entry */
226                     Entry = CONTAINING_RECORD(SplayLinks,
227                                               UNICODE_PREFIX_TABLE_ENTRY,
228                                               Links);
229 
230                     /* Set it as a root entry */
231                     Entry->NodeTypeCode = PFX_NTC_ROOT;
232 
233                     /* Add it to the root entries list */
234                     PreviousEntry->NextPrefixTree = Entry;
235                     Entry->NextPrefixTree = NextEntry;
236                 }
237 
238                 /* Return the entry */
239                 return Entry;
240             }
241 
242             /* We'll do a case-sensitive search if we've reached this point */
243             NextEntry = Entry;
244             do
245             {
246                 /* Do the case-sensitive search */
247                 Result = CompareUnicodeStrings(NextEntry->Prefix,
248                                                FullName,
249                                                CaseInsensitiveIndex);
250                 if ((Result != GenericLessThan) &&
251                     (Result != GenericGreaterThan))
252                 {
253                     /* This is a positive match, return it */
254                     return NextEntry;
255                 }
256 
257                 /* No match yet, continue looping the circular list */
258                 NextEntry = NextEntry->CaseMatch;
259             } while (NextEntry != Entry);
260 
261             /*
262              * If we got here, then we found a non-case-sensitive match, but
263              * we need to find a case-sensitive match, so we'll just keep
264              * searching the next tree (NOTE: we need to break out for this).
265              */
266             break;
267         }
268 
269         /* Splay links exhausted, move to next entry */
270         PreviousEntry = CurrentEntry;
271         CurrentEntry = CurrentEntry->NextPrefixTree;
272     }
273 
274     /* If we got here, nothing was found */
275     return NULL;
276 }
277 
278 /*
279  * @implemented
280  */
281 VOID
282 NTAPI
283 RtlInitializeUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable)
284 {
285     DPRINT("RtlInitializeUnicodePrefix(): Table %p\n",
286         PrefixTable);
287 
288     /* Setup the table */
289     PrefixTable->NameLength = 0;
290     PrefixTable->LastNextEntry = NULL;
291     PrefixTable->NodeTypeCode = PFX_NTC_TABLE;
292     PrefixTable->NextPrefixTree = (PUNICODE_PREFIX_TABLE_ENTRY)PrefixTable;
293 }
294 
295 /*
296  * @implemented
297  */
298 BOOLEAN
299 NTAPI
300 RtlInsertUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable,
301                        PUNICODE_STRING Prefix,
302                        PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry)
303 {
304     PUNICODE_PREFIX_TABLE_ENTRY CurrentEntry, PreviousEntry, Entry, NextEntry;
305     ULONG NameCount;
306     RTL_GENERIC_COMPARE_RESULTS Result;
307     PRTL_SPLAY_LINKS SplayLinks;
308 
309     DPRINT("RtlInsertUnicodePrefix(): Table %p, Prefix %wZ, "
310         "TableEntry %p\n", PrefixTable, Prefix, PrefixTableEntry);
311 
312     /* Find out how many names there are */
313     NameCount = ComputeUnicodeNameLength(Prefix);
314 
315     /* Set up the initial entry data */
316     PrefixTableEntry->NameLength = (CSHORT)NameCount;
317     PrefixTableEntry->Prefix = Prefix;
318     RtlInitializeSplayLinks(&PrefixTableEntry->Links);
319 
320     /* Find the right spot where to insert this entry */
321     PreviousEntry = (PUNICODE_PREFIX_TABLE_ENTRY)PrefixTable;
322     CurrentEntry = PreviousEntry->NextPrefixTree;
323     while (CurrentEntry->NameLength > (CSHORT)NameCount)
324     {
325         /* Not a match, move to the next entry */
326         PreviousEntry = CurrentEntry;
327         CurrentEntry = CurrentEntry->NextPrefixTree;
328     }
329 
330     /* Check if we did find a tree by now */
331     if (CurrentEntry->NameLength != (CSHORT)NameCount)
332     {
333         /* We didn't, so insert a new entry in the list */
334         PreviousEntry->NextPrefixTree = PrefixTableEntry;
335         PrefixTableEntry->NextPrefixTree = CurrentEntry;
336 
337         /* This is now a root entry with case match */
338         PrefixTableEntry->NodeTypeCode = PFX_NTC_ROOT;
339         PrefixTableEntry->CaseMatch = PrefixTableEntry;
340 
341         /* Quick return */
342         return TRUE;
343     }
344 
345     /* We found a tree, so start the search loop */
346     Entry = CurrentEntry;
347     while (TRUE)
348     {
349         /* Do a case-insensitive comparison to find out the match level */
350         Result = CompareUnicodeStrings(Entry->Prefix, Prefix, 0);
351         if (Result == GenericEqual)
352         {
353             /* We have a match, start doing a case-sensitive search */
354             NextEntry = Entry;
355 
356             /* Search the circular case-match list */
357             do
358             {
359                 /* Check if we found a match */
360                 if (CompareUnicodeStrings(NextEntry->Prefix, Prefix, -1) ==
361                     (GenericEqual))
362                 {
363                     /* We must fail the insert: it already exists */
364                     return FALSE;
365                 }
366 
367                 /* Move to the next entry in the circular list */
368                 NextEntry = NextEntry->CaseMatch;
369             }
370             while (NextEntry != Entry);
371 
372             /*
373              * No match found, so we can safely insert it. Remember that a
374              * case insensitive match was found, so this is not a ROOT NTC
375              * but a Case Match NTC instead.
376              */
377             PrefixTableEntry->NodeTypeCode = PFX_NTC_CASE_MATCH;
378             PrefixTableEntry->NextPrefixTree = NULL;
379 
380             /* Insert it into the circular list */
381             PrefixTableEntry->CaseMatch = Entry->CaseMatch;
382             Entry->CaseMatch = PrefixTableEntry;
383             break;
384         }
385 
386         /* Check if the result was greater or lesser than */
387         if (Result == GenericGreaterThan)
388         {
389             /* Check out if we have a left child */
390             if (RtlLeftChild(&Entry->Links))
391             {
392                 /* We do, enter it and restart the loop */
393                 SplayLinks = RtlLeftChild(&Entry->Links);
394                 Entry = CONTAINING_RECORD(SplayLinks,
395                                           UNICODE_PREFIX_TABLE_ENTRY,
396                                           Links);
397             }
398             else
399             {
400                 /* We don't, set this entry as a child */
401                 PrefixTableEntry->NodeTypeCode = PFX_NTC_CHILD;
402                 PrefixTableEntry->NextPrefixTree = NULL;
403                 PrefixTableEntry->CaseMatch = PrefixTableEntry;
404 
405                 /* Insert us into the tree */
406                 RtlInsertAsLeftChild(&Entry->Links, &PrefixTableEntry->Links);
407                 break;
408             }
409         }
410         else
411         {
412             /* Check out if we have a right child */
413             if (RtlRightChild(&Entry->Links))
414             {
415                 /* We do, enter it and restart the loop */
416                 SplayLinks = RtlRightChild(&Entry->Links);
417                 Entry = CONTAINING_RECORD(SplayLinks,
418                                           UNICODE_PREFIX_TABLE_ENTRY,
419                                           Links);
420             }
421             else
422             {
423                 /* We don't, set this entry as a child */
424                 PrefixTableEntry->NodeTypeCode = PFX_NTC_CHILD;
425                 PrefixTableEntry->NextPrefixTree = NULL;
426                 PrefixTableEntry->CaseMatch = PrefixTableEntry;
427 
428                 /* Insert us into the tree */
429                 RtlInsertAsRightChild(&Entry->Links, &PrefixTableEntry->Links);
430                 break;
431             }
432         }
433     }
434 
435     /* Get the next tree entry */
436     NextEntry = CurrentEntry->NextPrefixTree;
437 
438     /* Set what was the current entry to a child entry */
439     CurrentEntry->NodeTypeCode = PFX_NTC_CHILD;
440     CurrentEntry->NextPrefixTree = NULL;
441 
442     /* Splay the tree */
443     SplayLinks = RtlSplay(&Entry->Links);
444 
445     /* The link points to the root, get it */
446     Entry = CONTAINING_RECORD(SplayLinks, UNICODE_PREFIX_TABLE_ENTRY, Links);
447 
448     /* Mark the root as a root entry */
449     Entry->NodeTypeCode = PFX_NTC_ROOT;
450 
451     /* Add it to the tree list */
452     PreviousEntry->NextPrefixTree = Entry;
453     Entry->NextPrefixTree = NextEntry;
454 
455     /* Return success */
456     return TRUE;
457 }
458 
459 /*
460 * @implemented
461 */
462 PUNICODE_PREFIX_TABLE_ENTRY
463 NTAPI
464 RtlNextUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable,
465                      BOOLEAN Restart)
466 {
467     PRTL_SPLAY_LINKS SplayLinks;
468     PUNICODE_PREFIX_TABLE_ENTRY Entry, CaseMatchEntry = NULL;
469 
470     DPRINT("RtlNextUnicodePrefix(): Table %p Restart %u\n",
471         PrefixTable, Restart);
472 
473     /* We might need this entry 2/3rd of the time, so cache it now */
474     if (PrefixTable->LastNextEntry)
475         CaseMatchEntry = PrefixTable->LastNextEntry->CaseMatch;
476 
477     /* Check if this is a restart or if we don't have a last entry */
478     if ((Restart) || !(PrefixTable->LastNextEntry))
479     {
480         /* Get the next entry and validate it */
481         Entry = PrefixTable->NextPrefixTree;
482         if (Entry->NodeTypeCode == PFX_NTC_TABLE) return NULL;
483 
484         /* Now get the Splay Tree Links */
485         SplayLinks = &Entry->Links;
486 
487         /* Loop until we get the first node in the tree */
488         while (RtlLeftChild(SplayLinks)) SplayLinks = RtlLeftChild(SplayLinks);
489 
490         /* Get the entry from it */
491         Entry = CONTAINING_RECORD(SplayLinks,
492                                   UNICODE_PREFIX_TABLE_ENTRY,
493                                   Links);
494     }
495     else if (CaseMatchEntry->NodeTypeCode == PFX_NTC_CASE_MATCH)
496     {
497         /* If the last entry was a Case Match, then return it */
498         Entry = CaseMatchEntry;
499     }
500     else
501     {
502         /* Find the successor */
503         SplayLinks = RtlRealSuccessor(&CaseMatchEntry->Links);
504         if (!SplayLinks)
505         {
506             /* Didn't find one, we'll have to search the tree */
507             SplayLinks = &PrefixTable->LastNextEntry->Links;
508 
509             /* Get the topmost node (root) */
510             while (!RtlIsRoot(SplayLinks)) SplayLinks = RtlParent(SplayLinks);
511             Entry = CONTAINING_RECORD(SplayLinks,
512                                       UNICODE_PREFIX_TABLE_ENTRY,
513                                       Links);
514 
515             /* Get its tree and make sure somethign is in it */
516             Entry = Entry->NextPrefixTree;
517             if (Entry->NameLength <= 0) return NULL;
518 
519             /* Select these new links and find the first node */
520             while (RtlLeftChild(SplayLinks)) SplayLinks = RtlLeftChild(SplayLinks);
521         }
522 
523         /* Get the entry from it */
524         Entry = CONTAINING_RECORD(SplayLinks,
525                                   UNICODE_PREFIX_TABLE_ENTRY,
526                                   Links);
527     }
528 
529     /* Save this entry as the last one returned, and return it */
530     PrefixTable->LastNextEntry = Entry;
531     return Entry;
532 }
533 
534 /*
535  * @implemented
536  */
537 VOID
538 NTAPI
539 RtlRemoveUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable,
540                        PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry)
541 {
542     PUNICODE_PREFIX_TABLE_ENTRY Entry, RefEntry, NewEntry;
543     PRTL_SPLAY_LINKS SplayLinks;
544 
545     DPRINT("RtlRemoveUnicodePrefix(): Table %p, TableEntry %p\n",
546         PrefixTable, PrefixTableEntry);
547 
548     /* Erase the last entry */
549     PrefixTable->LastNextEntry = NULL;
550 
551     /* Check if this was a Case Match Entry */
552     if (PrefixTableEntry->NodeTypeCode == PFX_NTC_CASE_MATCH)
553     {
554         /* Get the case match entry */
555         Entry = PrefixTableEntry->CaseMatch;
556 
557         /* Now loop until we find one referencing what the caller sent */
558         while (Entry->CaseMatch != PrefixTableEntry) Entry = Entry->CaseMatch;
559 
560         /* We found the entry that was sent, link them to delete this entry */
561         Entry->CaseMatch = PrefixTableEntry->CaseMatch;
562     }
563     else if ((PrefixTableEntry->NodeTypeCode == PFX_NTC_ROOT) ||
564             (PrefixTableEntry->NodeTypeCode == PFX_NTC_CHILD))
565     {
566         /* Check if this entry is a case match */
567         if (PrefixTableEntry->CaseMatch != PrefixTableEntry)
568         {
569             /* Get the case match entry */
570             Entry = PrefixTableEntry->CaseMatch;
571 
572             /* Now loop until we find one referencing what the caller sent */
573             while (Entry->CaseMatch != PrefixTableEntry) Entry = Entry->CaseMatch;
574 
575             /* We found the entry that was sent, link them to delete this entry */
576             Entry->CaseMatch = PrefixTableEntry->CaseMatch;
577 
578             /* Copy the data */
579             Entry->NodeTypeCode = PrefixTableEntry->NodeTypeCode;
580             Entry->NextPrefixTree = PrefixTableEntry->NextPrefixTree;
581             Entry->Links = PrefixTableEntry->Links;
582 
583             /* Now check if we are a root entry */
584             if (RtlIsRoot(&PrefixTableEntry->Links))
585             {
586                 /* We are, so make this entry root as well */
587                 Entry->Links.Parent = &Entry->Links;
588 
589                 /* Find the entry referencing us */
590                 RefEntry = Entry->NextPrefixTree;
591                 while (RefEntry->NextPrefixTree != Entry)
592                 {
593                     /* Not this one, move to the next entry */
594                     RefEntry = RefEntry->NextPrefixTree;
595                 }
596 
597                 /* Link them to us now */
598                 RefEntry->NextPrefixTree = Entry;
599             }
600             else if (RtlIsLeftChild(&PrefixTableEntry->Links))
601             {
602                 /* We were the left child, so make us as well */
603                 RtlParent(&PrefixTableEntry->Links)->LeftChild = &Entry->Links;
604             }
605             else
606             {
607                 /* We were the right child, so make us as well */
608                 RtlParent(&PrefixTableEntry->Links)->RightChild = &Entry->Links;
609             }
610 
611             /* Check if we have a left child */
612             if (RtlLeftChild(&Entry->Links))
613             {
614                 /* Update its parent link */
615                 RtlLeftChild(&Entry->Links)->Parent = &Entry->Links;
616             }
617             /* Check if we have a right child */
618             if (RtlRightChild(&Entry->Links))
619             {
620                 /* Update its parent link */
621                 RtlRightChild(&Entry->Links)->Parent = &Entry->Links;
622             }
623         }
624         else
625         {
626             /* It's not a case match, so we'll delete the actual entry */
627             SplayLinks = &PrefixTableEntry->Links;
628 
629             /* Find the root entry */
630             while (!RtlIsRoot(SplayLinks)) SplayLinks = RtlParent(SplayLinks);
631             Entry = CONTAINING_RECORD(SplayLinks,
632                                       UNICODE_PREFIX_TABLE_ENTRY,
633                                       Links);
634 
635             /* Delete the entry and check if the whole tree is gone */
636             SplayLinks = RtlDelete(&PrefixTableEntry->Links);
637             if (!SplayLinks)
638             {
639                 /* The tree is also gone now, find the entry referencing us */
640                 RefEntry = Entry->NextPrefixTree;
641                 while (RefEntry->NextPrefixTree != Entry)
642                 {
643                     /* Not this one, move to the next entry */
644                     RefEntry = RefEntry->NextPrefixTree;
645                 }
646 
647                 /* Link them so this entry stops being referenced */
648                 RefEntry->NextPrefixTree = Entry->NextPrefixTree;
649             }
650             else if (&Entry->Links != SplayLinks)
651             {
652                 /* The tree is still here, but we got moved to a new one */
653                 NewEntry = CONTAINING_RECORD(SplayLinks,
654                                              UNICODE_PREFIX_TABLE_ENTRY,
655                                              Links);
656 
657                 /* Find the entry referencing us */
658                 RefEntry = Entry->NextPrefixTree;
659                 while (RefEntry->NextPrefixTree != Entry)
660                 {
661                     /* Not this one, move to the next entry */
662                     RefEntry = RefEntry->NextPrefixTree;
663                 }
664 
665                 /* Since we got moved, make us the new root entry */
666                 NewEntry->NodeTypeCode = PFX_NTC_ROOT;
667 
668                 /* Link us with the entry referencing the old root */
669                 RefEntry->NextPrefixTree = NewEntry;
670 
671                 /* And link us with the old tree */
672                 NewEntry->NextPrefixTree = Entry->NextPrefixTree;
673 
674                 /* Set the old tree as a child */
675                 Entry->NodeTypeCode = PFX_NTC_CHILD;
676                 Entry->NextPrefixTree = NULL;
677             }
678         }
679     }
680 }
681 
682 /* EOF */
683