Started implementing support for the SubSystemTib field in the TEB of
[wine] / dlls / ntdll / rtlstr.c
1 /*
2  * Rtl string functions
3  *
4  * Copyright (C) 1996-1998 Marcus Meissner
5  * Copyright (C) 2000      Alexandre Julliard
6  * Copyright (C) 2003      Thomas Mertes
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24
25 #include <assert.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
36 #include "ntdll_misc.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
39
40 #define GUID_STRING_LENGTH    38
41
42 UINT NlsAnsiCodePage = 0;
43 BYTE NlsMbCodePageTag = 0;
44 BYTE NlsMbOemCodePageTag = 0;
45
46 static const union cptable *ansi_table;
47 static const union cptable *oem_table;
48 static const union cptable* unix_table; /* NULL if UTF8 */
49
50
51 /**************************************************************************
52  *      __wine_init_codepages   (NTDLL.@)
53  *
54  * Set the code page once kernel32 is loaded. Should be done differently.
55  */
56 void __wine_init_codepages( const union cptable *ansi, const union cptable *oem,
57                             const union cptable *ucp)
58 {
59     ansi_table = ansi;
60     oem_table = oem;
61     unix_table = ucp;
62     NlsAnsiCodePage = ansi->info.codepage;
63 }
64
65 int ntdll_umbstowcs(DWORD flags, const char* src, int srclen, WCHAR* dst, int dstlen)
66
67     return (unix_table) ?
68         wine_cp_mbstowcs( unix_table, flags, src, srclen, dst, dstlen ) :
69         wine_utf8_mbstowcs( flags, src, srclen, dst, dstlen );
70 }
71
72 int ntdll_wcstoumbs(DWORD flags, const WCHAR* src, int srclen, char* dst, int dstlen,
73                     const char* defchar, int *used )
74
75     return (unix_table) ?
76         wine_cp_wcstombs( unix_table, flags, src, srclen, dst, dstlen, defchar, used ) :
77         wine_utf8_wcstombs( src, srclen, dst, dstlen );
78 }
79
80 /**************************************************************************
81  *      RtlInitAnsiString   (NTDLL.@)
82  *
83  * Initializes a buffered ansi string.
84  *
85  * RETURNS
86  *  Nothing.
87  *
88  * NOTES
89  *  Assigns source to target->Buffer. The length of source is assigned to
90  *  target->Length and target->MaximumLength. If source is NULL the length
91  *  of source is assumed to be 0.
92  */
93 void WINAPI RtlInitAnsiString(
94     PANSI_STRING target, /* [I/O] Buffered ansi string to be initialized */
95     PCSZ source)         /* [I]   '\0' terminated string used to initialize target */
96 {
97     if ((target->Buffer = (PCHAR) source))
98     {
99         target->Length = strlen(source);
100         target->MaximumLength = target->Length + 1;
101     }
102     else target->Length = target->MaximumLength = 0;
103 }
104
105
106 /**************************************************************************
107  *      RtlInitString   (NTDLL.@)
108  *
109  * Initializes a buffered string.
110  *
111  * RETURNS
112  *  Nothing.
113  *
114  * NOTES
115  *  Assigns source to target->Buffer. The length of source is assigned to
116  *  target->Length and target->MaximumLength. If source is NULL the length
117  *  of source is assumed to be 0.
118  */
119 void WINAPI RtlInitString(
120     PSTRING target, /* [I/O] Buffered string to be initialized */
121     PCSZ source)    /* [I]   '\0' terminated string used to initialize target */
122 {
123     RtlInitAnsiString( target, source );
124 }
125
126
127 /**************************************************************************
128  *      RtlFreeAnsiString   (NTDLL.@)
129  */
130 void WINAPI RtlFreeAnsiString( PSTRING str )
131 {
132     if (str->Buffer) RtlFreeHeap( GetProcessHeap(), 0, str->Buffer );
133 }
134
135
136 /**************************************************************************
137  *      RtlFreeOemString   (NTDLL.@)
138  */
139 void WINAPI RtlFreeOemString( PSTRING str )
140 {
141     RtlFreeAnsiString( str );
142 }
143
144
145 /**************************************************************************
146  *      RtlCopyString   (NTDLL.@)
147  */
148 void WINAPI RtlCopyString( STRING *dst, const STRING *src )
149 {
150     if (src)
151     {
152         unsigned int len = min( src->Length, dst->MaximumLength );
153         memcpy( dst->Buffer, src->Buffer, len );
154         dst->Length = len;
155     }
156     else dst->Length = 0;
157 }
158
159
160 /**************************************************************************
161  *      RtlInitUnicodeString   (NTDLL.@)
162  *
163  * Initializes a buffered unicode string.
164  *
165  * RETURNS
166  *  Nothing.
167  *
168  * NOTES
169  *  Assigns source to target->Buffer. The length of source is assigned to
170  *  target->Length and target->MaximumLength. If source is NULL the length
171  *  of source is assumed to be 0.
172  */
173 void WINAPI RtlInitUnicodeString(
174     PUNICODE_STRING target, /* [I/O] Buffered unicode string to be initialized */
175     PCWSTR source)          /* [I]   '\0' terminated unicode string used to initialize target */
176 {
177     if ((target->Buffer = (PWSTR) source))
178     {
179         target->Length = strlenW(source) * sizeof(WCHAR);
180         target->MaximumLength = target->Length + sizeof(WCHAR);
181     }
182     else target->Length = target->MaximumLength = 0;
183 }
184
185
186 /**************************************************************************
187  *      RtlInitUnicodeStringEx   (NTDLL.@)
188  *
189  * Initializes a buffered unicode string.
190  *
191  * RETURNS
192  *  Success: STATUS_SUCCESS. target is initialized.
193  *  Failure: STATUS_NAME_TOO_LONG, if the source string is larger than 65532 bytes.
194  *
195  * NOTES
196  *  Assigns source to target->Buffer. The length of source is assigned to
197  *  target->Length and target->MaximumLength. If source is NULL the length
198  *  of source is assumed to be 0.
199  */
200 NTSTATUS WINAPI RtlInitUnicodeStringEx(
201     PUNICODE_STRING target, /* [I/O] Buffered unicode string to be initialized */
202     PCWSTR source)          /* [I]   '\0' terminated unicode string used to initialize target */
203 {
204     if (source != NULL) {
205         unsigned int len = strlenW(source) * sizeof(WCHAR);
206
207         if (len > 0xFFFC) {
208             return STATUS_NAME_TOO_LONG;
209         } else {
210             target->Length = len;
211             target->MaximumLength = len + sizeof(WCHAR);
212             target->Buffer = (PWSTR) source;
213         } /* if */
214     } else {
215         target->Length = 0;
216         target->MaximumLength = 0;
217         target->Buffer = NULL;
218     } /* if */
219     return STATUS_SUCCESS;
220 }
221
222
223 /**************************************************************************
224  *      RtlCreateUnicodeString   (NTDLL.@)
225  *
226  * Creates a UNICODE_STRING from a null-terminated Unicode string.
227  *
228  * RETURNS
229  *     Success: TRUE
230  *     Failure: FALSE
231  */
232 BOOLEAN WINAPI RtlCreateUnicodeString( PUNICODE_STRING target, LPCWSTR src )
233 {
234     int len = (strlenW(src) + 1) * sizeof(WCHAR);
235     if (!(target->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, len ))) return FALSE;
236     memcpy( target->Buffer, src, len );
237     target->MaximumLength = len;
238     target->Length = len - sizeof(WCHAR);
239     return TRUE;
240 }
241
242
243 /**************************************************************************
244  *      RtlCreateUnicodeStringFromAsciiz   (NTDLL.@)
245  *
246  * Creates a UNICODE_STRING from a null-terminated Ascii string.
247  *
248  * RETURNS
249  *     Success: TRUE
250  *     Failure: FALSE
251  */
252 BOOLEAN WINAPI RtlCreateUnicodeStringFromAsciiz( PUNICODE_STRING target, LPCSTR src )
253 {
254     STRING ansi;
255     RtlInitAnsiString( &ansi, src );
256     return !RtlAnsiStringToUnicodeString( target, &ansi, TRUE );
257 }
258
259
260 /**************************************************************************
261  *      RtlFreeUnicodeString   (NTDLL.@)
262  *
263  * Frees a UNICODE_STRING created with RtlCreateUnicodeString() or 
264  * RtlCreateUnicodeStringFromAsciiz().
265  *
266  * RETURNS
267  *     nothing
268  */
269 void WINAPI RtlFreeUnicodeString( PUNICODE_STRING str )
270 {
271     if (str->Buffer) RtlFreeHeap( GetProcessHeap(), 0, str->Buffer );
272 }
273
274
275 /**************************************************************************
276  *      RtlCopyUnicodeString   (NTDLL.@)
277  *
278  * Copies from one UNICODE_STRING to another.
279  *
280  * RETURNS
281  *     nothing
282  */
283 void WINAPI RtlCopyUnicodeString( UNICODE_STRING *dst, const UNICODE_STRING *src )
284 {
285     if (src)
286     {
287         unsigned int len = min( src->Length, dst->MaximumLength );
288         memcpy( dst->Buffer, src->Buffer, len );
289         dst->Length = len;
290         /* append terminating '\0' if enough space */
291         if (len < dst->MaximumLength) dst->Buffer[len / sizeof(WCHAR)] = 0;
292     }
293     else dst->Length = 0;
294 }
295
296
297 /**************************************************************************
298  *      RtlDuplicateUnicodeString   (NTDLL.@)
299  *
300  * Duplicates an unicode string.
301  *
302  * RETURNS
303  *  Success: STATUS_SUCCESS. destination contains the duplicated unicode string.
304  *  Failure: STATUS_INVALID_PARAMETER, if one of the parameters is illegal.
305  *           STATUS_NO_MEMORY, if the allocation fails.
306  *
307  * NOTES
308  *  For add_nul there are several possible values:
309  *  0 = destination will not be '\0' terminated,
310  *  1 = destination will be '\0' terminated,
311  *  3 = like 1 but for an empty source string produce '\0' terminated empty
312  *     Buffer instead of assigning NULL to the Buffer.
313  *  Other add_nul values are invalid.
314  */
315 NTSTATUS WINAPI RtlDuplicateUnicodeString(
316     int add_nul,                  /* [I] flag */
317     const UNICODE_STRING *source, /* [I] Unicode string to be duplicated */
318     UNICODE_STRING *destination)  /* [O] destination for the duplicated unicode string */
319 {
320     if (source == NULL || destination == NULL ||
321         source->Length > source->MaximumLength ||
322         (source->Length == 0 && source->MaximumLength > 0 && source->Buffer == NULL) ||
323         add_nul == 2 || add_nul >= 4 || add_nul < 0) {
324         return STATUS_INVALID_PARAMETER;
325     } else {
326         if (source->Length == 0 && add_nul != 3) {
327             destination->Length = 0;
328             destination->MaximumLength = 0;
329             destination->Buffer = NULL;
330         } else {
331             unsigned int destination_max_len = source->Length;
332
333             if (add_nul) {
334                 destination_max_len += sizeof(WCHAR);
335             } /* if */
336             destination->Buffer = RtlAllocateHeap(GetProcessHeap(), 0, destination_max_len);
337             if (destination->Buffer == NULL) {
338                 return STATUS_NO_MEMORY;
339             } else {
340                 memcpy(destination->Buffer, source->Buffer, source->Length);
341                 destination->Length = source->Length;
342                 destination->MaximumLength = source->Length;
343                 /* append terminating '\0' if enough space */
344                 if (add_nul) {
345                     destination->MaximumLength = destination_max_len;
346                     destination->Buffer[destination->Length / sizeof(WCHAR)] = 0;
347                 } /* if */
348             } /* if */
349         } /* if */
350     } /* if */
351     return STATUS_SUCCESS;
352 }
353
354
355 /**************************************************************************
356  *      RtlEraseUnicodeString   (NTDLL.@)
357  *
358  * Overwrites a UNICODE_STRING with zeros.
359  *
360  * RETURNS
361  *     nothing
362  */
363 void WINAPI RtlEraseUnicodeString( UNICODE_STRING *str )
364 {
365     if (str->Buffer)
366     {
367         memset( str->Buffer, 0, str->MaximumLength );
368         str->Length = 0;
369     }
370 }
371
372
373 /*
374     COMPARISON FUNCTIONS
375 */
376
377
378 /******************************************************************************
379  *      RtlCompareString   (NTDLL.@)
380  */
381 LONG WINAPI RtlCompareString( const STRING *s1, const STRING *s2, BOOLEAN CaseInsensitive )
382 {
383     unsigned int len;
384     LONG ret = 0;
385     LPCSTR p1, p2;
386
387     len = min(s1->Length, s2->Length);
388     p1 = s1->Buffer;
389     p2 = s2->Buffer;
390
391     if (CaseInsensitive)
392     {
393         while (!ret && len--) ret = RtlUpperChar(*p1++) - RtlUpperChar(*p2++);
394     }
395     else
396     {
397         while (!ret && len--) ret = *p1++ - *p2++;
398     }
399     if (!ret) ret = s1->Length - s2->Length;
400     return ret;
401 }
402
403
404 /******************************************************************************
405  *      RtlCompareUnicodeString   (NTDLL.@)
406  */
407 LONG WINAPI RtlCompareUnicodeString( const UNICODE_STRING *s1, const UNICODE_STRING *s2,
408                                      BOOLEAN CaseInsensitive )
409 {
410     unsigned int len;
411     LONG ret = 0;
412     LPCWSTR p1, p2;
413
414     len = min(s1->Length, s2->Length) / sizeof(WCHAR);
415     p1 = s1->Buffer;
416     p2 = s2->Buffer;
417
418     if (CaseInsensitive)
419     {
420         while (!ret && len--) ret = toupperW(*p1++) - toupperW(*p2++);
421     }
422     else
423     {
424         while (!ret && len--) ret = *p1++ - *p2++;
425     }
426     if (!ret) ret = s1->Length - s2->Length;
427     return ret;
428 }
429
430
431 /**************************************************************************
432  *      RtlEqualString   (NTDLL.@)
433  *
434  * Determine if two strings are equal.
435  *
436  * PARAMS
437  *  s1              [I] Source string
438  *  s2              [I] String to compare to s1
439  *  CaseInsensitive [I] TRUE = Case insensitive, FALSE = Case sensitive
440  *
441  * RETURNS
442  *  Non-zero if s1 is equal to s2, 0 otherwise.
443  */
444 BOOLEAN WINAPI RtlEqualString( const STRING *s1, const STRING *s2, BOOLEAN CaseInsensitive )
445 {
446     if (s1->Length != s2->Length) return FALSE;
447     return !RtlCompareString( s1, s2, CaseInsensitive );
448 }
449
450
451 /**************************************************************************
452  *      RtlEqualUnicodeString   (NTDLL.@)
453  *
454  * Unicode version of RtlEqualString.
455  */
456 BOOLEAN WINAPI RtlEqualUnicodeString( const UNICODE_STRING *s1, const UNICODE_STRING *s2,
457                                       BOOLEAN CaseInsensitive )
458 {
459     if (s1->Length != s2->Length) return FALSE;
460     return !RtlCompareUnicodeString( s1, s2, CaseInsensitive );
461 }
462
463
464 /**************************************************************************
465  *      RtlPrefixString   (NTDLL.@)
466  *
467  * Determine if one string is a prefix of another.
468  *
469  * PARAMS
470  *  s1          [I] Prefix to look for in s2
471  *  s2          [I] String that may contain s1 as a prefix
472  *  ignore_case [I] TRUE = Case insensitive, FALSE = Case sensitive
473  *
474  * RETURNS
475  *  TRUE if s2 contains s1 as a prefix, FALSE otherwise.
476  */
477 BOOLEAN WINAPI RtlPrefixString( const STRING *s1, const STRING *s2, BOOLEAN ignore_case )
478 {
479     unsigned int i;
480
481     if (s1->Length > s2->Length) return FALSE;
482     if (ignore_case)
483     {
484         for (i = 0; i < s1->Length; i++)
485             if (RtlUpperChar(s1->Buffer[i]) != RtlUpperChar(s2->Buffer[i])) return FALSE;
486     }
487     else
488     {
489         for (i = 0; i < s1->Length; i++)
490             if (s1->Buffer[i] != s2->Buffer[i]) return FALSE;
491     }
492     return TRUE;
493 }
494
495
496 /**************************************************************************
497  *      RtlPrefixUnicodeString   (NTDLL.@)
498  *
499  * Unicode version of RtlPrefixString.
500  */
501 BOOLEAN WINAPI RtlPrefixUnicodeString( const UNICODE_STRING *s1,
502                                        const UNICODE_STRING *s2,
503                                        BOOLEAN ignore_case )
504 {
505     unsigned int i;
506
507     if (s1->Length > s2->Length) return FALSE;
508     if (ignore_case)
509     {
510         for (i = 0; i < s1->Length / sizeof(WCHAR); i++)
511             if (toupperW(s1->Buffer[i]) != toupperW(s2->Buffer[i])) return FALSE;
512     }
513     else
514     {
515         for (i = 0; i < s1->Length / sizeof(WCHAR); i++)
516             if (s1->Buffer[i] != s2->Buffer[i]) return FALSE;
517     }
518     return TRUE;
519 }
520
521
522 /**************************************************************************
523  *      RtlEqualComputerName   (NTDLL.@)
524  *
525  * Determine if two computer names are the same.
526  *
527  * PARAMS
528  *  left  [I] First computer name
529  *  right [I] Second computer name
530  *
531  * RETURNS
532  *  0 if the names are equal, non-zero otherwise.
533  *
534  * NOTES
535  *  The comparison is case insensitive.
536  */
537 NTSTATUS WINAPI RtlEqualComputerName(const UNICODE_STRING *left,
538                                      const UNICODE_STRING *right)
539 {
540     NTSTATUS ret;
541     STRING upLeft, upRight;
542
543     if (!(ret = RtlUpcaseUnicodeStringToOemString( &upLeft, left, TRUE )))
544     {
545        if (!(ret = RtlUpcaseUnicodeStringToOemString( &upRight, right, TRUE )))
546        {
547          ret = RtlEqualString( &upLeft, &upRight, FALSE );
548          RtlFreeOemString( &upRight );
549        }
550        RtlFreeOemString( &upLeft );
551     }
552     return ret;
553 }
554
555
556 /**************************************************************************
557  *      RtlEqualDomainName   (NTDLL.@)
558  *
559  * Determine if two domain names are the same.
560  *
561  * PARAMS
562  *  left  [I] First domain name
563  *  right [I] Second domain name
564  *
565  * RETURNS
566  *  0 if the names are equal, non-zero otherwise.
567  *
568  * NOTES
569  *  The comparison is case insensitive.
570  */
571 NTSTATUS WINAPI RtlEqualDomainName(const UNICODE_STRING *left,
572                                    const UNICODE_STRING *right)
573 {
574     return RtlEqualComputerName(left, right);
575 }
576
577
578 /*
579         COPY BETWEEN ANSI_STRING or UNICODE_STRING
580         there is no parameter checking, it just crashes
581 */
582
583
584 /**************************************************************************
585  *      RtlAnsiStringToUnicodeString   (NTDLL.@)
586  *
587  * Converts an ansi string to an unicode string.
588  *
589  * RETURNS
590  *  Success: STATUS_SUCCESS. uni contains the converted string
591  *  Failure: STATUS_BUFFER_OVERFLOW, if doalloc is FALSE and ansi is too small.
592  *           STATUS_NO_MEMORY, if doalloc is TRUE and the allocation fails.
593  *           STATUS_INVALID_PARAMETER_2, if the unicode string would be larger than 65535.
594  *
595  * NOTES
596  *  This function always writes a terminating '\0'.
597  */
598 NTSTATUS WINAPI RtlAnsiStringToUnicodeString(
599     PUNICODE_STRING uni, /* [I/O] Destination for the unicode string */
600     PCANSI_STRING ansi,  /* [I]   Ansi string to be converted */
601     BOOLEAN doalloc)     /* [I]   TRUE=Allocate new buffer for uni, FALSE=Use existing buffer */
602 {
603     DWORD total = RtlAnsiStringToUnicodeSize( ansi );
604
605     if (total > 0xffff) return STATUS_INVALID_PARAMETER_2;
606     uni->Length = total - sizeof(WCHAR);
607     if (doalloc)
608     {
609         uni->MaximumLength = total;
610         if (!(uni->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, total )))
611             return STATUS_NO_MEMORY;
612     }
613     else if (total > uni->MaximumLength) return STATUS_BUFFER_OVERFLOW;
614
615     RtlMultiByteToUnicodeN( uni->Buffer, uni->Length, NULL, ansi->Buffer, ansi->Length );
616     uni->Buffer[uni->Length / sizeof(WCHAR)] = 0;
617     return STATUS_SUCCESS;
618 }
619
620
621 /**************************************************************************
622  *      RtlOemStringToUnicodeString   (NTDLL.@)
623  *
624  * Converts an oem string to an unicode string.
625  *
626  * RETURNS
627  *  Success: STATUS_SUCCESS. uni contains the converted string
628  *  Failure: STATUS_BUFFER_OVERFLOW, if doalloc is FALSE and oem is too small.
629  *           STATUS_NO_MEMORY, if doalloc is TRUE and the allocation fails.
630  *           STATUS_INVALID_PARAMETER_2, if the unicode string would be larger than 65535.
631  *
632  * NOTES
633  *  This function always writes a terminating '\0'.
634  */
635 NTSTATUS WINAPI RtlOemStringToUnicodeString(
636     UNICODE_STRING *uni, /* [I/O] Destination for the unicode string */
637     const STRING *oem,   /* [I]   Oem string to be converted */
638     BOOLEAN doalloc)     /* [I]   TRUE=Allocate new buffer for uni, FALSE=Use existing buffer */
639 {
640     DWORD total = RtlOemStringToUnicodeSize( oem );
641
642     if (total > 0xffff) return STATUS_INVALID_PARAMETER_2;
643     uni->Length = total - sizeof(WCHAR);
644     if (doalloc)
645     {
646         uni->MaximumLength = total;
647         if (!(uni->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, total )))
648             return STATUS_NO_MEMORY;
649     }
650     else if (total > uni->MaximumLength) return STATUS_BUFFER_OVERFLOW;
651
652     RtlOemToUnicodeN( uni->Buffer, uni->Length, NULL, oem->Buffer, oem->Length );
653     uni->Buffer[uni->Length / sizeof(WCHAR)] = 0;
654     return STATUS_SUCCESS;
655 }
656
657
658 /**************************************************************************
659  *      RtlUnicodeStringToAnsiString   (NTDLL.@)
660  *
661  * Converts an unicode string to an ansi string.
662  *
663  * RETURNS
664  *  Success: STATUS_SUCCESS. ansi contains the converted string
665  *  Failure: STATUS_BUFFER_OVERFLOW, if doalloc is FALSE and ansi is too small.
666  *           STATUS_NO_MEMORY, if doalloc is TRUE and the allocation fails.
667  *
668  * NOTES
669  *  This function always writes a terminating '\0'.
670  *  It performs a partial copy if ansi is too small.
671  */
672 NTSTATUS WINAPI RtlUnicodeStringToAnsiString(
673     STRING *ansi,              /* [I/O] Destination for the ansi string */
674     const UNICODE_STRING *uni, /* [I]   Unicode string to be converted */
675     BOOLEAN doalloc)           /* [I]   TRUE=Allocate new buffer for ansi, FALSE=Use existing buffer */
676 {
677     NTSTATUS ret = STATUS_SUCCESS;
678     DWORD len = RtlUnicodeStringToAnsiSize( uni );
679
680     ansi->Length = len - 1;
681     if (doalloc)
682     {
683         ansi->MaximumLength = len;
684         if (!(ansi->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, len )))
685             return STATUS_NO_MEMORY;
686     }
687     else if (ansi->MaximumLength < len)
688     {
689         if (!ansi->MaximumLength) return STATUS_BUFFER_OVERFLOW;
690         ansi->Length = ansi->MaximumLength - 1;
691         ret = STATUS_BUFFER_OVERFLOW;
692     }
693
694     RtlUnicodeToMultiByteN( ansi->Buffer, ansi->Length, NULL, uni->Buffer, uni->Length );
695     ansi->Buffer[ansi->Length] = 0;
696     return ret;
697 }
698
699
700 /**************************************************************************
701  *      RtlUnicodeStringToOemString   (NTDLL.@)
702  *
703  * Converts a Rtl Unicode string to an OEM string.
704  *
705  * PARAMS
706  *  oem     [O] Destination for OEM string
707  *  uni     [I] Source Unicode string
708  *  doalloc [I] TRUE=Allocate new buffer for oem,FALSE=Use existing buffer
709  *
710  * RETURNS
711  *  Success: STATUS_SUCCESS. oem contains the converted string
712  *  Failure: STATUS_BUFFER_OVERFLOW, if doalloc is FALSE and oem is too small.
713  *           STATUS_NO_MEMORY, if doalloc is TRUE and allocation fails.
714  *
715  * NOTES
716  *   If doalloc is TRUE, the length allocated is uni->Length + 1.
717  *   This function always '\0' terminates the string returned.
718  */
719 NTSTATUS WINAPI RtlUnicodeStringToOemString( STRING *oem,
720                                              const UNICODE_STRING *uni,
721                                              BOOLEAN doalloc )
722 {
723     NTSTATUS ret = STATUS_SUCCESS;
724     DWORD len = RtlUnicodeStringToOemSize( uni );
725
726     oem->Length = len - 1;
727     if (doalloc)
728     {
729         oem->MaximumLength = len;
730         if (!(oem->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, len )))
731             return STATUS_NO_MEMORY;
732     }
733     else if (oem->MaximumLength < len)
734     {
735         if (!oem->MaximumLength) return STATUS_BUFFER_OVERFLOW;
736         oem->Length = oem->MaximumLength - 1;
737         ret = STATUS_BUFFER_OVERFLOW;
738     }
739
740     RtlUnicodeToOemN( oem->Buffer, oem->Length, NULL, uni->Buffer, uni->Length );
741     oem->Buffer[oem->Length] = 0;
742     return ret;
743 }
744
745
746 /**************************************************************************
747  *      RtlMultiByteToUnicodeN   (NTDLL.@)
748  *
749  * Converts a multi-byte string to a Unicode string.
750  *
751  * RETURNS
752  *  NTSTATUS code
753  *
754  * NOTES
755  *  Performs a partial copy if dst is too small.
756  */
757 NTSTATUS WINAPI RtlMultiByteToUnicodeN( LPWSTR dst, DWORD dstlen, LPDWORD reslen,
758                                         LPCSTR src, DWORD srclen )
759 {
760
761     int ret = wine_cp_mbstowcs( ansi_table, 0, src, srclen, dst, dstlen/sizeof(WCHAR) );
762     if (reslen)
763         *reslen = (ret >= 0) ? ret*sizeof(WCHAR) : dstlen; /* overflow -> we filled up to dstlen */
764     return STATUS_SUCCESS;
765 }
766
767
768 /**************************************************************************
769  *      RtlOemToUnicodeN   (NTDLL.@)
770  *
771  * Converts a multi-byte string in the OEM code page to a Unicode string.
772  *
773  * RETURNS
774  *  NTSTATUS code
775  */
776 NTSTATUS WINAPI RtlOemToUnicodeN( LPWSTR dst, DWORD dstlen, LPDWORD reslen,
777                                   LPCSTR src, DWORD srclen )
778 {
779     int ret = wine_cp_mbstowcs( oem_table, 0, src, srclen, dst, dstlen/sizeof(WCHAR) );
780     if (reslen)
781         *reslen = (ret >= 0) ? ret*sizeof(WCHAR) : dstlen; /* overflow -> we filled up to dstlen */
782     return STATUS_SUCCESS;
783 }
784
785
786 /**************************************************************************
787  *      RtlUnicodeToMultiByteN   (NTDLL.@)
788  *
789  * Converts a Unicode string to a multi-byte string in the ANSI code page.
790  *
791  * RETURNS
792  *  NTSTATUS code
793  */
794 NTSTATUS WINAPI RtlUnicodeToMultiByteN( LPSTR dst, DWORD dstlen, LPDWORD reslen,
795                                         LPCWSTR src, DWORD srclen )
796 {
797     int ret = wine_cp_wcstombs( ansi_table, 0, src, srclen / sizeof(WCHAR),
798                                 dst, dstlen, NULL, NULL );
799     if (reslen)
800         *reslen = (ret >= 0) ? ret : dstlen; /* overflow -> we filled up to dstlen */
801     return STATUS_SUCCESS;
802 }
803
804
805 /**************************************************************************
806  *      RtlUnicodeToOemN   (NTDLL.@)
807  *
808  * Converts a Unicode string to a multi-byte string in the OEM code page.
809  *
810  * RETURNS
811  *  NTSTATUS code
812  */
813 NTSTATUS WINAPI RtlUnicodeToOemN( LPSTR dst, DWORD dstlen, LPDWORD reslen,
814                                   LPCWSTR src, DWORD srclen )
815 {
816     int ret = wine_cp_wcstombs( oem_table, 0, src, srclen / sizeof(WCHAR),
817                                 dst, dstlen, NULL, NULL );
818     if (reslen)
819         *reslen = (ret >= 0) ? ret : dstlen; /* overflow -> we filled up to dstlen */
820     return STATUS_SUCCESS;
821 }
822
823
824 /*
825      CASE CONVERSIONS
826 */
827
828
829 /**************************************************************************
830  *      RtlUpperChar   (NTDLL.@)
831  *
832  * Converts an Ascii character to uppercase.
833  *
834  * PARAMS
835  *  ch [I] Character to convert
836  *
837  * RETURNS
838  *  The uppercase character value.
839  *
840  * NOTES
841  *  For the input characters from 'a' .. 'z' it returns 'A' .. 'Z'.
842  *  All other input characters are returned unchanged. The locale and
843  *  multibyte characters are not taken into account (as native DLL).
844  */
845 CHAR WINAPI RtlUpperChar( CHAR ch )
846 {
847     if (ch >= 'a' && ch <= 'z') {
848         return ch - 'a' + 'A';
849     } else {
850         return ch;
851     } /* if */
852 }
853
854
855 /**************************************************************************
856  *      RtlUpperString   (NTDLL.@)
857  *
858  * Converts an Ascii string to uppercase.
859  *
860  * PARAMS
861  *  dst [O] Destination for converted string
862  *  src [I] Source string to convert
863  *
864  * RETURNS
865  *  Nothing.
866  *
867  * NOTES
868  *  For the src characters from 'a' .. 'z' it assigns 'A' .. 'Z' to dst.
869  *  All other src characters are copied unchanged to dst. The locale and
870  *  multibyte characters are not taken into account (as native DLL).
871  *  The number of character copied is the minimum of src->Length and
872  *  the dst->MaximumLength.
873  */
874 void WINAPI RtlUpperString( STRING *dst, const STRING *src )
875 {
876     unsigned int i, len = min(src->Length, dst->MaximumLength);
877
878     for (i = 0; i < len; i++) dst->Buffer[i] = RtlUpperChar(src->Buffer[i]);
879     dst->Length = len;
880 }
881
882
883 /**************************************************************************
884  *      RtlUpcaseUnicodeChar   (NTDLL.@)
885  *
886  * Converts an Unicode character to uppercase.
887  *
888  * PARAMS
889  *  wch [I] Character to convert
890  *
891  * RETURNS
892  *  The uppercase character value.
893  */
894 WCHAR WINAPI RtlUpcaseUnicodeChar( WCHAR wch )
895 {
896     return toupperW(wch);
897 }
898
899
900 /**************************************************************************
901  *      RtlDowncaseUnicodeChar   (NTDLL.@)
902  *
903  * Converts an Unicode character to lowercase.
904  *
905  * PARAMS
906  *  wch [I] Character to convert
907  *
908  * RETURNS
909  *  The lowercase character value.
910  */
911 WCHAR WINAPI RtlDowncaseUnicodeChar(WCHAR wch)
912 {
913     return tolowerW(wch);
914 }
915
916
917 /**************************************************************************
918  *      RtlUpcaseUnicodeString   (NTDLL.@)
919  *
920  * Converts an Unicode string to uppercase.
921  *
922  * PARAMS
923  *  dest    [O] Destination for converted string
924  *  src     [I] Source string to convert
925  *  doalloc [I] TRUE=Allocate a buffer for dest if it doesn't have one
926  *
927  * RETURNS
928  *  Success: STATUS_SUCCESS. dest contains the converted string.
929  *  Failure: STATUS_NO_MEMORY, if doalloc is TRUE and memory allocation fails, or
930  *           STATUS_BUFFER_OVERFLOW, if doalloc is FALSE and dest is too small.
931  *
932  * NOTES
933  *  dest is never '\0' terminated because it may be equal to src, and src
934  *  might not be '\0' terminated. dest->Length is only set upon success.
935  */
936 NTSTATUS WINAPI RtlUpcaseUnicodeString( UNICODE_STRING *dest,
937                                         const UNICODE_STRING *src,
938                                         BOOLEAN doalloc)
939 {
940     DWORD i, len = src->Length;
941
942     if (doalloc)
943     {
944         dest->MaximumLength = len;
945         if (!(dest->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, len )))
946             return STATUS_NO_MEMORY;
947     }
948     else if (len > dest->MaximumLength) return STATUS_BUFFER_OVERFLOW;
949
950     for (i = 0; i < len/sizeof(WCHAR); i++) dest->Buffer[i] = toupperW(src->Buffer[i]);
951     dest->Length = len;
952     return STATUS_SUCCESS;
953 }
954
955
956 /**************************************************************************
957  *      RtlDowncaseUnicodeString   (NTDLL.@)
958  *
959  * Converts an Unicode string to lowercase.
960  *
961  * PARAMS
962  *  dest    [O] Destination for converted string
963  *  src     [I] Source string to convert
964  *  doalloc [I] TRUE=Allocate a buffer for dest if it doesn't have one
965  *
966  * RETURNS
967  *  Success: STATUS_SUCCESS. dest contains the converted string.
968  *  Failure: STATUS_NO_MEMORY, if doalloc is TRUE and memory allocation fails, or
969  *           STATUS_BUFFER_OVERFLOW, if doalloc is FALSE and dest is too small.
970  *
971  * NOTES
972  *  dest is never '\0' terminated because it may be equal to src, and src
973  *  might not be '\0' terminated. dest->Length is only set upon success.
974  */
975 NTSTATUS WINAPI RtlDowncaseUnicodeString(
976     UNICODE_STRING *dest,
977     const UNICODE_STRING *src,
978     BOOLEAN doalloc)
979 {
980     DWORD i;
981     DWORD len = src->Length;
982
983     if (doalloc) {
984         dest->MaximumLength = len;
985         if (!(dest->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, len ))) {
986             return STATUS_NO_MEMORY;
987         } /* if */
988     } else if (len > dest->MaximumLength) {
989         return STATUS_BUFFER_OVERFLOW;
990     } /* if */
991
992     for (i = 0; i < len/sizeof(WCHAR); i++) {
993         dest->Buffer[i] = tolowerW(src->Buffer[i]);
994     } /* for */
995     dest->Length = len;
996     return STATUS_SUCCESS;
997 }
998
999
1000 /**************************************************************************
1001  *      RtlUpcaseUnicodeStringToAnsiString   (NTDLL.@)
1002  *
1003  * Converts a Unicode string to the equivalent ANSI upper-case representation.
1004  *
1005  * RETURNS
1006  *  NTSTATUS code
1007  *
1008  * NOTES
1009  *  writes terminating 0
1010  */
1011 NTSTATUS WINAPI RtlUpcaseUnicodeStringToAnsiString( STRING *dst,
1012                                                     const UNICODE_STRING *src,
1013                                                     BOOLEAN doalloc )
1014 {
1015     NTSTATUS ret;
1016     UNICODE_STRING upcase;
1017
1018     if (!(ret = RtlUpcaseUnicodeString( &upcase, src, TRUE )))
1019     {
1020         ret = RtlUnicodeStringToAnsiString( dst, &upcase, doalloc );
1021         RtlFreeUnicodeString( &upcase );
1022     }
1023     return ret;
1024 }
1025
1026
1027 /**************************************************************************
1028  *      RtlUpcaseUnicodeStringToOemString   (NTDLL.@)
1029  *
1030  * Converts a UNICODE_STRING to the equivalent OEM upper-case representation
1031  * stored in STRING format.
1032  *
1033  * RETURNS
1034  *  NTSTATUS code
1035  *
1036  * NOTES
1037  *  writes terminating 0
1038  */
1039 NTSTATUS WINAPI RtlUpcaseUnicodeStringToOemString( STRING *dst,
1040                                                    const UNICODE_STRING *src,
1041                                                    BOOLEAN doalloc )
1042 {
1043     NTSTATUS ret;
1044     UNICODE_STRING upcase;
1045
1046     if (!(ret = RtlUpcaseUnicodeString( &upcase, src, TRUE )))
1047     {
1048         ret = RtlUnicodeStringToOemString( dst, &upcase, doalloc );
1049         RtlFreeUnicodeString( &upcase );
1050     }
1051     return ret;
1052 }
1053
1054
1055 /**************************************************************************
1056  *      RtlUpcaseUnicodeStringToCountedOemString   (NTDLL.@)
1057  *
1058  * Converts a UNICODE_STRING to the equivalent OEM upper-case representation
1059  * stored in STRING format.
1060  *
1061  * RETURNS
1062  *  NTSTATUS code
1063  *
1064  * NOTES
1065  *  Same as RtlUpcaseUnicodeStringToOemString but doesn't write terminating null
1066  */
1067 NTSTATUS WINAPI RtlUpcaseUnicodeStringToCountedOemString( STRING *oem,
1068                                                           const UNICODE_STRING *uni,
1069                                                           BOOLEAN doalloc )
1070 {
1071     NTSTATUS ret;
1072     UNICODE_STRING upcase;
1073
1074     if (!(ret = RtlUpcaseUnicodeString( &upcase, uni, TRUE )))
1075     {
1076         DWORD len = RtlUnicodeStringToOemSize( &upcase ) - 1;
1077         oem->Length = len;
1078         if (doalloc)
1079         {
1080             oem->MaximumLength = len;
1081             if (!(oem->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, len )))
1082             {
1083                 ret = STATUS_NO_MEMORY;
1084                 goto done;
1085             }
1086         }
1087         else if (oem->MaximumLength < len)
1088         {
1089             ret = STATUS_BUFFER_OVERFLOW;
1090             oem->Length = oem->MaximumLength;
1091             if (!oem->MaximumLength) goto done;
1092         }
1093         RtlUnicodeToOemN( oem->Buffer, oem->Length, NULL, upcase.Buffer, upcase.Length );
1094     done:
1095         RtlFreeUnicodeString( &upcase );
1096     }
1097     return ret;
1098 }
1099
1100
1101 /**************************************************************************
1102  *      RtlUpcaseUnicodeToMultiByteN   (NTDLL.@)
1103  *
1104  * Converts a Unicode string to the equivalent ANSI upper-case representation.
1105  *
1106  * RETURNS
1107  *  NTSTATUS code
1108  */
1109 NTSTATUS WINAPI RtlUpcaseUnicodeToMultiByteN( LPSTR dst, DWORD dstlen, LPDWORD reslen,
1110                                               LPCWSTR src, DWORD srclen )
1111 {
1112     NTSTATUS ret;
1113     LPWSTR upcase;
1114     DWORD i;
1115
1116     if (!(upcase = RtlAllocateHeap( GetProcessHeap(), 0, srclen ))) return STATUS_NO_MEMORY;
1117     for (i = 0; i < srclen/sizeof(WCHAR); i++) upcase[i] = toupperW(src[i]);
1118     ret = RtlUnicodeToMultiByteN( dst, dstlen, reslen, upcase, srclen );
1119     RtlFreeHeap( GetProcessHeap(), 0, upcase );
1120     return ret;
1121 }
1122
1123
1124 /**************************************************************************
1125  *      RtlUpcaseUnicodeToOemN   (NTDLL.@)
1126  *
1127  * Converts a Unicode string to the equivalent OEM upper-case representation.
1128  *
1129  * RETURNS
1130  *  NTSTATUS code
1131  */
1132 NTSTATUS WINAPI RtlUpcaseUnicodeToOemN( LPSTR dst, DWORD dstlen, LPDWORD reslen,
1133                                         LPCWSTR src, DWORD srclen )
1134 {
1135     NTSTATUS ret;
1136     LPWSTR upcase;
1137     DWORD i;
1138
1139     if (!(upcase = RtlAllocateHeap( GetProcessHeap(), 0, srclen ))) return STATUS_NO_MEMORY;
1140     for (i = 0; i < srclen/sizeof(WCHAR); i++) upcase[i] = toupperW(src[i]);
1141     ret = RtlUnicodeToOemN( dst, dstlen, reslen, upcase, srclen );
1142     RtlFreeHeap( GetProcessHeap(), 0, upcase );
1143     return ret;
1144 }
1145
1146
1147 /*
1148         STRING SIZE
1149 */
1150
1151
1152 /**************************************************************************
1153  *      RtlOemStringToUnicodeSize   (NTDLL.@)
1154  *      RtlxOemStringToUnicodeSize  (NTDLL.@)
1155  *
1156  * Calculate the size in bytes necessary for the Unicode conversion of str,
1157  * including the terminating '\0'.
1158  *
1159  * PARAMS
1160  *  str [I] String to calculate the size of
1161  *
1162  * RETURNS
1163  *  The calculated size.
1164  */
1165 UINT WINAPI RtlOemStringToUnicodeSize( const STRING *str )
1166 {
1167     int ret = wine_cp_mbstowcs( oem_table, 0, str->Buffer, str->Length, NULL, 0 );
1168     return (ret + 1) * sizeof(WCHAR);
1169 }
1170
1171
1172 /**************************************************************************
1173  *      RtlAnsiStringToUnicodeSize   (NTDLL.@)
1174  *      RtlxAnsiStringToUnicodeSize  (NTDLL.@)
1175  *
1176  * Calculate the size in bytes necessary for the Unicode conversion of str,
1177  * including the terminating '\0'.
1178  *
1179  * PARAMS
1180  *  str [I] String to calculate the size of
1181  *
1182  * RETURNS
1183  *  The calculated size.
1184  */
1185 DWORD WINAPI RtlAnsiStringToUnicodeSize( const STRING *str )
1186 {
1187     DWORD ret;
1188     RtlMultiByteToUnicodeSize( &ret, str->Buffer, str->Length );
1189     return ret + sizeof(WCHAR);
1190 }
1191
1192
1193 /**************************************************************************
1194  *      RtlMultiByteToUnicodeSize   (NTDLL.@)
1195  *
1196  * Compute the size in bytes necessary for the Unicode conversion of str,
1197  * without the terminating '\0'.
1198  *
1199  * PARAMS
1200  *  size [O] Destination for size
1201  *  str  [I] String to calculate the size of
1202  *  len  [I] Length of str
1203  *
1204  * RETURNS
1205  *  STATUS_SUCCESS.
1206  */
1207 NTSTATUS WINAPI RtlMultiByteToUnicodeSize( DWORD *size, LPCSTR str, UINT len )
1208 {
1209     *size = wine_cp_mbstowcs( ansi_table, 0, str, len, NULL, 0 ) * sizeof(WCHAR);
1210     return STATUS_SUCCESS;
1211 }
1212
1213
1214 /**************************************************************************
1215  *      RtlUnicodeToMultiByteSize   (NTDLL.@)
1216  *
1217  * Calculate the size in bytes necessary for the multibyte conversion of str,
1218  * without the terminating '\0'.
1219  *
1220  * PARAMS
1221  *  size [O] Destination for size
1222  *  str  [I] String to calculate the size of
1223  *  len  [I] Length of str
1224  *
1225  * RETURNS
1226  *  STATUS_SUCCESS.
1227  */
1228 NTSTATUS WINAPI RtlUnicodeToMultiByteSize( PULONG size, LPCWSTR str, ULONG len )
1229 {
1230     *size = wine_cp_wcstombs( ansi_table, 0, str, len / sizeof(WCHAR), NULL, 0, NULL, NULL );
1231     return STATUS_SUCCESS;
1232 }
1233
1234
1235 /**************************************************************************
1236  *      RtlUnicodeStringToAnsiSize   (NTDLL.@)
1237  *      RtlxUnicodeStringToAnsiSize  (NTDLL.@)
1238  *
1239  * Calculate the size in bytes necessary for the Ansi conversion of str,
1240  * including the terminating '\0'.
1241  *
1242  * PARAMS
1243  *  str [I] String to calculate the size of
1244  *
1245  * RETURNS
1246  *  The calculated size.
1247  */
1248 DWORD WINAPI RtlUnicodeStringToAnsiSize( const UNICODE_STRING *str )
1249 {
1250     DWORD ret;
1251     RtlUnicodeToMultiByteSize( &ret, str->Buffer, str->Length );
1252     return ret + 1;
1253 }
1254
1255
1256 /**************************************************************************
1257  *      RtlUnicodeStringToOemSize   (NTDLL.@)
1258  *      RtlxUnicodeStringToOemSize  (NTDLL.@)
1259  *
1260  * Calculate the size in bytes necessary for the OEM conversion of str,
1261  * including the terminating '\0'.
1262  *
1263  * PARAMS
1264  *  str [I] String to calculate the size of
1265  *
1266  * RETURNS
1267  *  The calculated size.
1268  */
1269 DWORD WINAPI RtlUnicodeStringToOemSize( const UNICODE_STRING *str )
1270 {
1271     return wine_cp_wcstombs( oem_table, 0, str->Buffer, str->Length / sizeof(WCHAR),
1272                              NULL, 0, NULL, NULL ) + 1;
1273 }
1274
1275
1276 /**************************************************************************
1277  *      RtlAppendAsciizToString   (NTDLL.@)
1278  *
1279  * Concatenates a buffered character string and a '\0' terminated character
1280  * string
1281  *
1282  * RETURNS
1283  *  Success: STATUS_SUCCESS. src is appended to dest.
1284  *  Failure: STATUS_BUFFER_TOO_SMALL, if the buffer of dest is to small
1285  *                  to hold the concatenated string.
1286  *
1287  * NOTES
1288  *  if src is NULL dest is unchanged.
1289  *  dest is never '\0' terminated.
1290  */
1291 NTSTATUS WINAPI RtlAppendAsciizToString(
1292     STRING *dest, /* [I/O] Buffered character string to which src is concatenated */
1293     LPCSTR src)   /* [I]   '\0' terminated character string to be concatenated */
1294 {
1295     if (src != NULL) {
1296         unsigned int src_len = strlen(src);
1297         unsigned int dest_len  = src_len + dest->Length;
1298
1299         if (dest_len > dest->MaximumLength) return STATUS_BUFFER_TOO_SMALL;
1300         memcpy(dest->Buffer + dest->Length, src, src_len);
1301         dest->Length = dest_len;
1302     } /* if */
1303     return STATUS_SUCCESS;
1304 }
1305
1306
1307 /**************************************************************************
1308  *      RtlAppendStringToString   (NTDLL.@)
1309  *
1310  * Concatenates two buffered character strings
1311  *
1312  * RETURNS
1313  *  Success: STATUS_SUCCESS. src is appended to dest.
1314  *  Failure: STATUS_BUFFER_TOO_SMALL, if the buffer of dest is to small
1315  *                  to hold the concatenated string.
1316  *
1317  * NOTES
1318  *  if src->length is zero dest is unchanged.
1319  *  dest is never '\0' terminated.
1320  */
1321 NTSTATUS WINAPI RtlAppendStringToString(
1322     STRING *dest,       /* [I/O] Buffered character string to which src is concatenated */
1323     const STRING *src)  /* [I]   Buffered character string to be concatenated */
1324 {
1325     if (src->Length != 0) {
1326         unsigned int dest_len = src->Length + dest->Length;
1327
1328         if (dest_len > dest->MaximumLength) return STATUS_BUFFER_TOO_SMALL;
1329         memcpy(dest->Buffer + dest->Length, src->Buffer, src->Length);
1330         dest->Length = dest_len;
1331     } /* if */
1332     return STATUS_SUCCESS;
1333 }
1334
1335
1336 /**************************************************************************
1337  *      RtlAppendUnicodeToString   (NTDLL.@)
1338  *
1339  * Concatenates an buffered unicode string and a '\0' terminated unicode 
1340  * string
1341  *
1342  * RETURNS
1343  *  Success: STATUS_SUCCESS. src is appended to dest.
1344  *  Failure: STATUS_BUFFER_TOO_SMALL, if the buffer of dest is to small
1345  *                  to hold the concatenated string.
1346  *
1347  * NOTES
1348  *  if src is NULL dest is unchanged.
1349  *  dest is '\0' terminated when the MaximumLength allowes it.
1350  *  When dest fits exactly in MaximumLength characters the '\0' is ommitted.
1351  *
1352  * DIFFERENCES
1353  *  Does not write in the src->Buffer beyond MaximumLength when
1354  *  MaximumLength is odd as the native function does.
1355  */
1356 NTSTATUS WINAPI RtlAppendUnicodeToString(
1357     UNICODE_STRING *dest, /* [I/O] Buffered unicode string to which src is concatenated */
1358     LPCWSTR src)          /* [I]   '\0' terminated unicode string to be concatenated */
1359 {
1360     if (src != NULL) {
1361         unsigned int src_len = strlenW(src) * sizeof(WCHAR);
1362         unsigned int dest_len  = src_len + dest->Length;
1363
1364         if (dest_len > dest->MaximumLength) return STATUS_BUFFER_TOO_SMALL;
1365         memcpy(dest->Buffer + dest->Length/sizeof(WCHAR), src, src_len);
1366         dest->Length = dest_len;
1367         /* append terminating '\0' if enough space */
1368         if (dest_len + sizeof(WCHAR) <= dest->MaximumLength) {
1369             dest->Buffer[dest_len / sizeof(WCHAR)] = 0;
1370         } /* if */
1371     } /* if */
1372     return STATUS_SUCCESS;
1373 }
1374
1375
1376 /**************************************************************************
1377  *      RtlAppendUnicodeStringToString   (NTDLL.@)
1378  *
1379  * Concatenates two buffered unicode strings
1380  *
1381  * RETURNS
1382  *  Success: STATUS_SUCCESS. src is appended to dest.
1383  *  Failure: STATUS_BUFFER_TOO_SMALL, if the buffer of dest is to small
1384  *                  to hold the concatenated string.
1385  *
1386  * NOTES
1387  *  if src->length is zero dest is unchanged.
1388  *  dest is '\0' terminated when the MaximumLength allowes it.
1389  *  When dest fits exactly in MaximumLength characters the '\0' is ommitted.
1390  *
1391  * DIFFERENCES
1392  *  Does not write in the src->Buffer beyond MaximumLength when
1393  *  MaximumLength is odd as the native function does.
1394  */
1395 NTSTATUS WINAPI RtlAppendUnicodeStringToString(
1396     UNICODE_STRING *dest,      /* [I/O] Buffered unicode string to which src is concatenated */
1397     const UNICODE_STRING *src) /* [I]   Buffered unicode string to be concatenated */
1398 {
1399     if (src->Length != 0) {
1400         unsigned int dest_len = src->Length + dest->Length;
1401
1402         if (dest_len > dest->MaximumLength) return STATUS_BUFFER_TOO_SMALL;
1403         memcpy(dest->Buffer + dest->Length/sizeof(WCHAR), src->Buffer, src->Length);
1404         dest->Length = dest_len;
1405         /* append terminating '\0' if enough space */
1406         if (dest_len + sizeof(WCHAR) <= dest->MaximumLength) {
1407             dest->Buffer[dest_len / sizeof(WCHAR)] = 0;
1408         } /* if */
1409     } /* if */
1410     return STATUS_SUCCESS;
1411 }
1412
1413
1414 /**************************************************************************
1415  *      RtlFindCharInUnicodeString   (NTDLL.@)
1416  *
1417  * Searches for one of several unicode characters in an unicode string.
1418  *
1419  * RETURNS
1420  *  Success: STATUS_SUCCESS. pos contains the position after the character found.
1421  *  Failure: STATUS_NOT_FOUND, if none of the search_chars are in main_str.
1422  */
1423 NTSTATUS WINAPI RtlFindCharInUnicodeString(
1424     int flags,                          /* [I] Flags */
1425     const UNICODE_STRING *main_str,     /* [I] Unicode string in which one or more characters are searched */
1426     const UNICODE_STRING *search_chars, /* [I] Unicode string which contains the characters to search for */
1427     USHORT *pos)                        /* [O] Position of the first character found + 2 */
1428 {
1429     int main_idx;
1430     unsigned int search_idx;
1431
1432     switch (flags) {
1433         case 0:
1434             for (main_idx = 0; main_idx < main_str->Length / sizeof(WCHAR); main_idx++) {
1435                 for (search_idx = 0; search_idx < search_chars->Length / sizeof(WCHAR); search_idx++) {
1436                     if (main_str->Buffer[main_idx] == search_chars->Buffer[search_idx]) {
1437                         *pos = (main_idx + 1) * sizeof(WCHAR);
1438                         return STATUS_SUCCESS;
1439                     }
1440                 }
1441             }
1442             *pos = 0;
1443             return STATUS_NOT_FOUND;
1444         case 1:
1445             for (main_idx = main_str->Length / sizeof(WCHAR) - 1; main_idx >= 0; main_idx--) {
1446                 for (search_idx = 0; search_idx < search_chars->Length / sizeof(WCHAR); search_idx++) {
1447                     if (main_str->Buffer[main_idx] == search_chars->Buffer[search_idx]) {
1448                         *pos = main_idx * sizeof(WCHAR);
1449                         return STATUS_SUCCESS;
1450                     }
1451                 }
1452             }
1453             *pos = 0;
1454             return STATUS_NOT_FOUND;
1455         case 2:
1456             for (main_idx = 0; main_idx < main_str->Length / sizeof(WCHAR); main_idx++) {
1457                 search_idx = 0;
1458                 while (search_idx < search_chars->Length / sizeof(WCHAR) &&
1459                          main_str->Buffer[main_idx] != search_chars->Buffer[search_idx]) {
1460                     search_idx++;
1461                 }
1462                 if (search_idx >= search_chars->Length / sizeof(WCHAR)) {
1463                     *pos = (main_idx + 1) * sizeof(WCHAR);
1464                     return STATUS_SUCCESS;
1465                 }
1466             }
1467             *pos = 0;
1468             return STATUS_NOT_FOUND;
1469         case 3:
1470             for (main_idx = main_str->Length / sizeof(WCHAR) - 1; main_idx >= 0; main_idx--) {
1471                 search_idx = 0;
1472                 while (search_idx < search_chars->Length / sizeof(WCHAR) &&
1473                          main_str->Buffer[main_idx] != search_chars->Buffer[search_idx]) {
1474                     search_idx++;
1475                 }
1476                 if (search_idx >= search_chars->Length / sizeof(WCHAR)) {
1477                     *pos = main_idx * sizeof(WCHAR);
1478                     return STATUS_SUCCESS;
1479                 }
1480             }
1481             *pos = 0;
1482             return STATUS_NOT_FOUND;
1483     } /* switch */
1484     return STATUS_NOT_FOUND;
1485 }
1486
1487
1488 /*
1489         MISC
1490 */
1491 /* Tests that we currently implement */
1492 #define ITU_IMPLEMENTED_TESTS \
1493     (IS_TEXT_UNICODE_SIGNATURE | \
1494      IS_TEXT_UNICODE_REVERSE_SIGNATURE | \
1495      IS_TEXT_UNICODE_ODD_LENGTH | \
1496      IS_TEXT_UNICODE_STATISTICS | \
1497      IS_TEXT_UNICODE_NULL_BYTES)
1498
1499 /**************************************************************************
1500  *      RtlIsTextUnicode (NTDLL.@)
1501  *
1502  * Attempt to guess whether a text buffer is Unicode.
1503  *
1504  * PARAMS
1505  *  buf [I] Text buffer to test
1506  *  len [I] Length of buf
1507  *  pf  [O] Destination for test results
1508  *
1509  * RETURNS
1510  *  The length of the string if all tests were passed, 0 otherwise.
1511  *
1512  * FIXME
1513  *  Should implement more tests.
1514  */
1515 DWORD WINAPI RtlIsTextUnicode(
1516         LPVOID buf,
1517         DWORD len,
1518         DWORD *pf)
1519 {
1520         LPWSTR s = buf;
1521         DWORD flags = -1, out_flags = 0;
1522
1523         if (!len)
1524                 goto out;
1525         if (pf)
1526                 flags = *pf;
1527         /*
1528          * Apply various tests to the text string. According to the
1529          * docs, each test "passed" sets the corresponding flag in
1530          * the output flags. But some of the tests are mutually
1531          * exclusive, so I don't see how you could pass all tests ...
1532          */
1533
1534         /* Check for an odd length ... pass if even. */
1535         if ((flags & IS_TEXT_UNICODE_ODD_LENGTH) && (len & 1))
1536                 out_flags |= IS_TEXT_UNICODE_ODD_LENGTH;
1537
1538         /* Check for the special byte order unicode marks. */
1539         if ((flags & IS_TEXT_UNICODE_SIGNATURE) && *s == 0xFEFF)
1540                 out_flags |= IS_TEXT_UNICODE_SIGNATURE;
1541
1542     if ((flags & IS_TEXT_UNICODE_REVERSE_SIGNATURE) && *s == 0xFFFE)
1543         out_flags |= IS_TEXT_UNICODE_REVERSE_SIGNATURE;
1544
1545     /* apply some statistical analysis */
1546     if (flags & IS_TEXT_UNICODE_STATISTICS)
1547     {
1548         DWORD i, stats = 0;
1549         /* FIXME: checks only for ASCII characters in the unicode stream */
1550         for (i = 0; i < len / sizeof(WCHAR); i++)
1551         {
1552             if (s[i] <= 255) stats++;
1553         }
1554         if (stats > len / sizeof(WCHAR) / 2)
1555             out_flags |= IS_TEXT_UNICODE_STATISTICS;
1556     }
1557
1558     /* Check for unicode NULL chars */
1559     if (flags & IS_TEXT_UNICODE_NULL_BYTES)
1560     {
1561         DWORD i;
1562         for (i = 0; i < len / sizeof(WCHAR); i++)
1563         {
1564             if (!s[i])
1565             {
1566                 out_flags |= IS_TEXT_UNICODE_NULL_BYTES;
1567                 break;
1568             }
1569         }
1570     }
1571
1572         /*
1573          * Check whether the string passed all of the tests.
1574          */
1575         flags &= ITU_IMPLEMENTED_TESTS;
1576         if ((out_flags & flags) != flags)
1577                 len = 0;
1578 out:
1579         if (pf)
1580                 *pf = out_flags;
1581         return len;
1582 }
1583
1584
1585 /**************************************************************************
1586  *      RtlCharToInteger   (NTDLL.@)
1587  *
1588  * Converts a character string into its integer equivalent.
1589  *
1590  * RETURNS
1591  *  Success: STATUS_SUCCESS. value contains the converted number
1592  *  Failure: STATUS_INVALID_PARAMETER, if base is not 0, 2, 8, 10 or 16.
1593  *           STATUS_ACCESS_VIOLATION, if value is NULL.
1594  *
1595  * NOTES
1596  *  For base 0 it uses 10 as base and the string should be in the format
1597  *      "{whitespace} [+|-] [0[x|o|b]] {digits}".
1598  *  For other bases the string should be in the format
1599  *      "{whitespace} [+|-] {digits}".
1600  *  No check is made for value overflow, only the lower 32 bits are assigned.
1601  *  If str is NULL it crashes, as the native function does.
1602  *
1603  * DIFFERENCES
1604  *  This function does not read garbage behind '\0' as the native version does.
1605  */
1606 NTSTATUS WINAPI RtlCharToInteger(
1607     PCSZ str,      /* [I] '\0' terminated single-byte string containing a number */
1608     ULONG base,    /* [I] Number base for conversion (allowed 0, 2, 8, 10 or 16) */
1609     ULONG *value)  /* [O] Destination for the converted value */
1610 {
1611     CHAR chCurrent;
1612     int digit;
1613     ULONG RunningTotal = 0;
1614     char bMinus = 0;
1615
1616     while (*str != '\0' && *str <= ' ') {
1617         str++;
1618     } /* while */
1619
1620     if (*str == '+') {
1621         str++;
1622     } else if (*str == '-') {
1623         bMinus = 1;
1624         str++;
1625     } /* if */
1626
1627     if (base == 0) {
1628         base = 10;
1629         if (str[0] == '0') {
1630             if (str[1] == 'b') {
1631                 str += 2;
1632                 base = 2;
1633             } else if (str[1] == 'o') {
1634                 str += 2;
1635                 base = 8;
1636             } else if (str[1] == 'x') {
1637                 str += 2;
1638                 base = 16;
1639             } /* if */
1640         } /* if */
1641     } else if (base != 2 && base != 8 && base != 10 && base != 16) {
1642         return STATUS_INVALID_PARAMETER;
1643     } /* if */
1644
1645     if (value == NULL) {
1646         return STATUS_ACCESS_VIOLATION;
1647     } /* if */
1648
1649     while (*str != '\0') {
1650         chCurrent = *str;
1651         if (chCurrent >= '0' && chCurrent <= '9') {
1652             digit = chCurrent - '0';
1653         } else if (chCurrent >= 'A' && chCurrent <= 'Z') {
1654             digit = chCurrent - 'A' + 10;
1655         } else if (chCurrent >= 'a' && chCurrent <= 'z') {
1656             digit = chCurrent - 'a' + 10;
1657         } else {
1658             digit = -1;
1659         } /* if */
1660         if (digit < 0 || digit >= base) {
1661             *value = bMinus ? -RunningTotal : RunningTotal;
1662             return STATUS_SUCCESS;
1663         } /* if */
1664
1665         RunningTotal = RunningTotal * base + digit;
1666         str++;
1667     } /* while */
1668
1669     *value = bMinus ? -RunningTotal : RunningTotal;
1670     return STATUS_SUCCESS;
1671 }
1672
1673
1674 /**************************************************************************
1675  *      RtlIntegerToChar   (NTDLL.@)
1676  *
1677  * Converts an unsigned integer to a character string.
1678  *
1679  * RETURNS
1680  *  Success: STATUS_SUCCESS. str contains the converted number
1681  *  Failure: STATUS_INVALID_PARAMETER, if base is not 0, 2, 8, 10 or 16.
1682  *           STATUS_BUFFER_OVERFLOW, if str would be larger than length.
1683  *           STATUS_ACCESS_VIOLATION, if str is NULL.
1684  *
1685  * NOTES
1686  *  Instead of base 0 it uses 10 as base.
1687  *  Writes at most length characters to the string str.
1688  *  Str is '\0' terminated when length allowes it.
1689  *  When str fits exactly in length characters the '\0' is ommitted.
1690  */
1691 NTSTATUS WINAPI RtlIntegerToChar(
1692     ULONG value,   /* [I] Value to be converted */
1693     ULONG base,    /* [I] Number base for conversion (allowed 0, 2, 8, 10 or 16) */
1694     ULONG length,  /* [I] Length of the str buffer in bytes */
1695     PCHAR str)     /* [O] Destination for the converted value */
1696 {
1697     CHAR buffer[33];
1698     PCHAR pos;
1699     CHAR digit;
1700     ULONG len;
1701
1702     if (base == 0) {
1703         base = 10;
1704     } else if (base != 2 && base != 8 && base != 10 && base != 16) {
1705         return STATUS_INVALID_PARAMETER;
1706     } /* if */
1707
1708     pos = &buffer[32];
1709     *pos = '\0';
1710
1711     do {
1712         pos--;
1713         digit = value % base;
1714         value = value / base;
1715         if (digit < 10) {
1716             *pos = '0' + digit;
1717         } else {
1718             *pos = 'A' + digit - 10;
1719         } /* if */
1720     } while (value != 0L);
1721
1722     len = &buffer[32] - pos;
1723     if (len > length) {
1724         return STATUS_BUFFER_OVERFLOW;
1725     } else if (str == NULL) {
1726         return STATUS_ACCESS_VIOLATION;
1727     } else if (len == length) {
1728         memcpy(str, pos, len);
1729     } else {
1730         memcpy(str, pos, len + 1);
1731     } /* if */
1732     return STATUS_SUCCESS;
1733 }
1734
1735
1736 /**************************************************************************
1737  *      RtlUnicodeStringToInteger (NTDLL.@)
1738  *
1739  * Converts an unicode string into its integer equivalent.
1740  *
1741  * RETURNS
1742  *  Success: STATUS_SUCCESS. value contains the converted number
1743  *  Failure: STATUS_INVALID_PARAMETER, if base is not 0, 2, 8, 10 or 16.
1744  *           STATUS_ACCESS_VIOLATION, if value is NULL.
1745  *
1746  * NOTES
1747  *  For base 0 it uses 10 as base and the string should be in the format
1748  *      "{whitespace} [+|-] [0[x|o|b]] {digits}".
1749  *  For other bases the string should be in the format
1750  *      "{whitespace} [+|-] {digits}".
1751  *  No check is made for value overflow, only the lower 32 bits are assigned.
1752  *  If str is NULL it crashes, as the native function does.
1753  *
1754  * DIFFERENCES
1755  *  This function does not read garbage on string length 0 as the native
1756  *  version does.
1757  */
1758 NTSTATUS WINAPI RtlUnicodeStringToInteger(
1759     const UNICODE_STRING *str, /* [I] Unicode string to be converted */
1760     ULONG base,                /* [I] Number base for conversion (allowed 0, 2, 8, 10 or 16) */
1761     ULONG *value)              /* [O] Destination for the converted value */
1762 {
1763     LPWSTR lpwstr = str->Buffer;
1764     USHORT CharsRemaining = str->Length / sizeof(WCHAR);
1765     WCHAR wchCurrent;
1766     int digit;
1767     ULONG RunningTotal = 0;
1768     char bMinus = 0;
1769
1770     while (CharsRemaining >= 1 && *lpwstr <= ' ') {
1771         lpwstr++;
1772         CharsRemaining--;
1773     } /* while */
1774
1775     if (CharsRemaining >= 1) {
1776         if (*lpwstr == '+') {
1777             lpwstr++;
1778             CharsRemaining--;
1779         } else if (*lpwstr == '-') {
1780             bMinus = 1;
1781             lpwstr++;
1782             CharsRemaining--;
1783         } /* if */
1784     } /* if */
1785
1786     if (base == 0) {
1787         base = 10;
1788         if (CharsRemaining >= 2 && lpwstr[0] == '0') {
1789             if (lpwstr[1] == 'b') {
1790                 lpwstr += 2;
1791                 CharsRemaining -= 2;
1792                 base = 2;
1793             } else if (lpwstr[1] == 'o') {
1794                 lpwstr += 2;
1795                 CharsRemaining -= 2;
1796                 base = 8;
1797             } else if (lpwstr[1] == 'x') {
1798                 lpwstr += 2;
1799                 CharsRemaining -= 2;
1800                 base = 16;
1801             } /* if */
1802         } /* if */
1803     } else if (base != 2 && base != 8 && base != 10 && base != 16) {
1804         return STATUS_INVALID_PARAMETER;
1805     } /* if */
1806
1807     if (value == NULL) {
1808         return STATUS_ACCESS_VIOLATION;
1809     } /* if */
1810
1811     while (CharsRemaining >= 1) {
1812         wchCurrent = *lpwstr;
1813         if (wchCurrent >= '0' && wchCurrent <= '9') {
1814             digit = wchCurrent - '0';
1815         } else if (wchCurrent >= 'A' && wchCurrent <= 'Z') {
1816             digit = wchCurrent - 'A' + 10;
1817         } else if (wchCurrent >= 'a' && wchCurrent <= 'z') {
1818             digit = wchCurrent - 'a' + 10;
1819         } else {
1820             digit = -1;
1821         } /* if */
1822         if (digit < 0 || digit >= base) {
1823             *value = bMinus ? -RunningTotal : RunningTotal;
1824             return STATUS_SUCCESS;
1825         } /* if */
1826
1827         RunningTotal = RunningTotal * base + digit;
1828         lpwstr++;
1829         CharsRemaining--;
1830     } /* while */
1831
1832     *value = bMinus ? -RunningTotal : RunningTotal;
1833     return STATUS_SUCCESS;
1834 }
1835
1836
1837 /**************************************************************************
1838  *      RtlIntegerToUnicodeString (NTDLL.@)
1839  *
1840  * Converts an unsigned integer to a '\0' terminated unicode string.
1841  *
1842  * RETURNS
1843  *  Success: STATUS_SUCCESS. str contains the converted number
1844  *  Failure: STATUS_INVALID_PARAMETER, if base is not 0, 2, 8, 10 or 16.
1845  *           STATUS_BUFFER_OVERFLOW, if str is too small to hold the string
1846  *                  (with the '\0' termination). In this case str->Length
1847  *                  is set to the length, the string would have (which can
1848  *                  be larger than the MaximumLength).
1849  *
1850  * NOTES
1851  *  Instead of base 0 it uses 10 as base.
1852  *  If str is NULL it crashes, as the native function does.
1853  *
1854  * DIFFERENCES
1855  *  Do not return STATUS_BUFFER_OVERFLOW when the string is long enough.
1856  *  The native function does this when the string would be longer than 16
1857  *  characters even when the string parameter is long enough.
1858  */
1859 NTSTATUS WINAPI RtlIntegerToUnicodeString(
1860     ULONG value,         /* [I] Value to be converted */
1861     ULONG base,          /* [I] Number base for conversion (allowed 0, 2, 8, 10 or 16) */
1862     UNICODE_STRING *str) /* [O] Destination for the converted value */
1863 {
1864     WCHAR buffer[33];
1865     PWCHAR pos;
1866     WCHAR digit;
1867
1868     if (base == 0) {
1869         base = 10;
1870     } else if (base != 2 && base != 8 && base != 10 && base != 16) {
1871         return STATUS_INVALID_PARAMETER;
1872     } /* if */
1873
1874     pos = &buffer[32];
1875     *pos = '\0';
1876
1877     do {
1878         pos--;
1879         digit = value % base;
1880         value = value / base;
1881         if (digit < 10) {
1882             *pos = '0' + digit;
1883         } else {
1884             *pos = 'A' + digit - 10;
1885         } /* if */
1886     } while (value != 0L);
1887
1888     str->Length = (&buffer[32] - pos) * sizeof(WCHAR);
1889     if (str->Length >= str->MaximumLength) {
1890         return STATUS_BUFFER_OVERFLOW;
1891     } else {
1892         memcpy(str->Buffer, pos, str->Length + sizeof(WCHAR));
1893     } /* if */
1894     return STATUS_SUCCESS;
1895 }
1896
1897
1898 /*************************************************************************
1899  * RtlGUIDFromString (NTDLL.@)
1900  *
1901  * Convert a string representation of a GUID into a GUID.
1902  *
1903  * PARAMS
1904  *  str  [I] String representation in the format "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
1905  *  guid [O] Destination for the converted GUID
1906  *
1907  * RETURNS
1908  *  Success: STATUS_SUCCESS. guid contains the converted value.
1909  *  Failure: STATUS_INVALID_PARAMETER, if str is not in the expected format.
1910  *
1911  * SEE ALSO
1912  *  See RtlStringFromGUID.
1913  */
1914 NTSTATUS WINAPI RtlGUIDFromString(const UNICODE_STRING *str, GUID* guid)
1915 {
1916   int i = 0;
1917   const WCHAR *lpszCLSID = str->Buffer;
1918   BYTE* lpOut = (BYTE*)guid;
1919
1920   TRACE("(%s,%p)\n", debugstr_us(str), guid);
1921
1922   /* Convert string: {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
1923    * to memory:       DWORD... WORD WORD BYTES............
1924    */
1925   while (i < 37)
1926   {
1927     switch (i)
1928     {
1929     case 0:
1930       if (*lpszCLSID != '{')
1931         return STATUS_INVALID_PARAMETER;
1932       break;
1933
1934     case 9: case 14: case 19: case 24:
1935       if (*lpszCLSID != '-')
1936         return STATUS_INVALID_PARAMETER;
1937       break;
1938
1939     case 37:
1940       if (*lpszCLSID != '}')
1941         return STATUS_INVALID_PARAMETER;
1942       break;
1943
1944     default:
1945       {
1946         WCHAR ch = *lpszCLSID, ch2 = lpszCLSID[1];
1947         unsigned char byte;
1948
1949         /* Read two hex digits as a byte value */
1950         if      (ch >= '0' && ch <= '9') ch = ch - '0';
1951         else if (ch >= 'a' && ch <= 'f') ch = ch - 'a' + 10;
1952         else if (ch >= 'A' && ch <= 'F') ch = ch - 'A' + 10;
1953         else return STATUS_INVALID_PARAMETER;
1954
1955         if      (ch2 >= '0' && ch2 <= '9') ch2 = ch2 - '0';
1956         else if (ch2 >= 'a' && ch2 <= 'f') ch2 = ch2 - 'a' + 10;
1957         else if (ch2 >= 'A' && ch2 <= 'F') ch2 = ch2 - 'A' + 10;
1958         else return STATUS_INVALID_PARAMETER;
1959
1960         byte = ch << 4 | ch2;
1961
1962         switch (i)
1963         {
1964 #ifndef WORDS_BIGENDIAN
1965         /* For Big Endian machines, we store the data such that the
1966          * dword/word members can be read as DWORDS and WORDS correctly. */
1967         /* Dword */
1968         case 1:  lpOut[3] = byte; break;
1969         case 3:  lpOut[2] = byte; break;
1970         case 5:  lpOut[1] = byte; break;
1971         case 7:  lpOut[0] = byte; lpOut += 4;  break;
1972         /* Word */
1973         case 10: case 15: lpOut[1] = byte; break;
1974         case 12: case 17: lpOut[0] = byte; lpOut += 2; break;
1975 #endif
1976         /* Byte */
1977         default: lpOut[0] = byte; lpOut++; break;
1978         }
1979         lpszCLSID++; /* Skip 2nd character of byte */
1980         i++;
1981       }
1982     }
1983     lpszCLSID++;
1984     i++;
1985   }
1986
1987   return STATUS_SUCCESS;
1988 }
1989
1990 /*************************************************************************
1991  * RtlStringFromGUID (NTDLL.@)
1992  *
1993  * Convert a GUID into a string representation of a GUID.
1994  *
1995  * PARAMS
1996  *  guid [I] GUID to convert
1997  *  str  [O] Destination for the converted string
1998  *
1999  * RETURNS
2000  *  Success: STATUS_SUCCESS. str contains the converted value.
2001  *  Failure: STATUS_NO_MEMORY, if memory for str cannot be allocated.
2002  *
2003  * SEE ALSO
2004  *  See RtlGUIDFromString.
2005  */
2006 NTSTATUS WINAPI RtlStringFromGUID(const GUID* guid, UNICODE_STRING *str)
2007 {
2008   static const WCHAR szFormat[] = { '{','%','0','8','l','X','-',
2009     '%','0','4','X','-',  '%','0','4','X','-','%','0','2','X','%','0','2','X',
2010     '-',   '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
2011     '%','0','2','X','%','0','2','X','}','\0' };
2012
2013   TRACE("(%p,%p)\n", guid, str);
2014
2015   str->Length = GUID_STRING_LENGTH * sizeof(WCHAR);
2016   str->MaximumLength = str->Length + sizeof(WCHAR);
2017   str->Buffer = (WCHAR*)RtlAllocateHeap(GetProcessHeap(), 0, str->MaximumLength);
2018   if (!str->Buffer)
2019   {
2020     str->Length = str->MaximumLength = 0;
2021     return STATUS_NO_MEMORY;
2022   }
2023   sprintfW(str->Buffer, szFormat, guid->Data1, guid->Data2, guid->Data3,
2024           guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
2025           guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
2026
2027   return STATUS_SUCCESS;
2028 }