Make use of the DEFAULT_DEBUG_CHANNEL where appropriate.
[wine] / memory / string.c
1 /*
2  * String functions
3  *
4  * Copyright 1993 Yngvi Sigurjonsson
5  * Copyright 1996 Alexandre Julliard
6  */
7
8 #include <ctype.h>
9 #include <string.h>
10
11 #include "wine/winbase16.h"
12 #include "wine/winuser16.h"
13 #include "winbase.h"
14 #include "winuser.h"
15 #include "wine/keyboard16.h"
16 #include "wine/exception.h"
17 #include "winerror.h"
18 #include "crtdll.h"
19 #include "ldt.h"
20 #include "debugtools.h"
21 #include "winnls.h"
22
23 DEFAULT_DEBUG_CHANNEL(string)
24
25 static const BYTE STRING_Oem2Ansi[256] =
26 "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\244"
27 "\020\021\022\023\266\247\026\027\030\031\032\033\034\035\036\037"
28 "\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
29 "\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
30 "\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117"
31 "\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137"
32 "\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
33 "\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177"
34 "\307\374\351\342\344\340\345\347\352\353\350\357\356\354\304\305"
35 "\311\346\306\364\366\362\373\371\377\326\334\242\243\245\120\203"
36 "\341\355\363\372\361\321\252\272\277\137\254\275\274\241\253\273"
37 "\137\137\137\246\246\246\246\053\053\246\246\053\053\053\053\053"
38 "\053\055\055\053\055\053\246\246\053\053\055\055\246\055\053\055"
39 "\055\055\055\053\053\053\053\053\053\053\053\137\137\246\137\137"
40 "\137\337\137\266\137\137\265\137\137\137\137\137\137\137\137\137"
41 "\137\261\137\137\137\137\367\137\260\225\267\137\156\262\137\137";
42
43 static const BYTE STRING_Ansi2Oem[256] =
44 "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
45 "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
46 "\040\041\042\043\044\045\046\047\050\051\052\053\054\055\056\057"
47 "\060\061\062\063\064\065\066\067\070\071\072\073\074\075\076\077"
48 "\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117"
49 "\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137"
50 "\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157"
51 "\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177"
52 "\200\201\054\237\054\137\375\374\210\045\123\074\117\215\216\217"
53 "\220\140\047\042\042\371\055\137\230\231\163\076\157\235\236\131"
54 "\040\255\233\234\017\235\335\025\042\143\246\256\252\055\162\137"
55 "\370\361\375\063\047\346\024\372\054\061\247\257\254\253\137\250"
56 "\101\101\101\101\216\217\222\200\105\220\105\105\111\111\111\111"
57 "\104\245\117\117\117\117\231\170\117\125\125\125\232\131\137\341"
58 "\205\240\203\141\204\206\221\207\212\202\210\211\215\241\214\213"
59 "\144\244\225\242\223\157\224\366\157\227\243\226\201\171\137\230";
60
61 #define OEM_TO_ANSI(ch) (STRING_Oem2Ansi[(unsigned char)(ch)])
62 #define ANSI_TO_OEM(ch) (STRING_Ansi2Oem[(unsigned char)(ch)])
63
64 /* Internaly used by strchr family functions */
65 static BOOL ChrCmpA( WORD word1, WORD word2);
66
67
68 /* filter for page-fault exceptions */
69 static WINE_EXCEPTION_FILTER(page_fault)
70 {
71     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
72         return EXCEPTION_EXECUTE_HANDLER;
73     return EXCEPTION_CONTINUE_SEARCH;
74 }
75
76
77 /***********************************************************************
78  *           hmemcpy   (KERNEL.348)
79  */
80 void WINAPI hmemcpy16( LPVOID dst, LPCVOID src, LONG count )
81 {
82     memcpy( dst, src, count );
83 }
84
85
86 /***********************************************************************
87  *           lstrcat16   (KERNEL.89)
88  */
89 SEGPTR WINAPI lstrcat16( SEGPTR dst, LPCSTR src )
90 {
91     /* Windows does not check for NULL pointers here, so we don't either */
92     strcat( (LPSTR)PTR_SEG_TO_LIN(dst), src );
93     return dst;
94 }
95
96
97 /***********************************************************************
98  *           lstrcatA   (KERNEL32.599)
99  */
100 LPSTR WINAPI lstrcatA( LPSTR dst, LPCSTR src )
101 {
102     __TRY
103     {
104         strcat( dst, src );
105     }
106     __EXCEPT(page_fault)
107     {
108         SetLastError( ERROR_INVALID_PARAMETER );
109         return NULL;
110     }
111     __ENDTRY
112     return dst;
113 }
114
115
116 /***********************************************************************
117  *           lstrcatW   (KERNEL32.600)
118  */
119 LPWSTR WINAPI lstrcatW( LPWSTR dst, LPCWSTR src )
120 {
121     __TRY
122     {
123         CRTDLL_wcscat( dst, src );
124     }
125     __EXCEPT(page_fault)
126     {
127         SetLastError( ERROR_INVALID_PARAMETER );
128         return NULL;
129     }
130     __ENDTRY
131     return dst;
132 }
133
134
135 /***********************************************************************
136  *           lstrcatn16   (KERNEL.352)
137  */
138 SEGPTR WINAPI lstrcatn16( SEGPTR dst, LPCSTR src, INT16 n )
139 {
140     LPSTR p = (LPSTR)PTR_SEG_TO_LIN(dst);
141
142     while (*p) p++;
143     if ((n -= (p - (LPSTR)PTR_SEG_TO_LIN(dst))) <= 0) return dst;
144     lstrcpynA( p, src, n );
145     return dst;
146 }
147
148
149 /***********************************************************************
150  *           lstrcmp16   (USER.430)
151  */
152 INT16 WINAPI lstrcmp16( LPCSTR str1, LPCSTR str2 )
153 {
154     return (INT16)strcmp( str1, str2 );
155 }
156
157
158 /***********************************************************************
159  *           lstrcmp32A   (KERNEL.602)
160  */
161 INT WINAPI lstrcmpA( LPCSTR str1, LPCSTR str2 )
162 {
163     return CompareStringA(LOCALE_SYSTEM_DEFAULT,0,str1,-1,str2,-1) - 2 ;
164 }
165
166
167 /***********************************************************************
168  *           lstrcmp32W   (KERNEL.603)
169  * FIXME : should call CompareString32W, when it is implemented.
170  *    This implementation is not "word sort", as it should.
171  */
172 INT WINAPI lstrcmpW( LPCWSTR str1, LPCWSTR str2 )
173 {
174     TRACE("L%s and L%s\n",
175                    debugstr_w (str1), debugstr_w (str2));
176     if (!str1 || !str2) {
177         SetLastError(ERROR_INVALID_PARAMETER);
178         return 0;
179     }
180     while (*str1 && (*str1 == *str2)) { str1++; str2++; }
181     return (INT)(*str1 - *str2);
182 }
183
184
185 /***********************************************************************
186  *           lstrcmpi16   (USER.471)
187  */
188 INT16 WINAPI lstrcmpi16( LPCSTR str1, LPCSTR str2 )
189 {
190     return (INT16)lstrcmpiA( str1, str2 );
191 }
192
193
194 /***********************************************************************
195  *           lstrcmpi32A   (KERNEL32.605)
196  */
197 INT WINAPI lstrcmpiA( LPCSTR str1, LPCSTR str2 )
198 {    TRACE("strcmpi %s and %s\n",
199                    debugstr_a (str1), debugstr_a (str2));
200     return CompareStringA(LOCALE_SYSTEM_DEFAULT,NORM_IGNORECASE,str1,-1,str2,-1)-2;
201 }
202
203
204 /***********************************************************************
205  *           lstrcmpi32W   (KERNEL32.606)
206  */
207 INT WINAPI lstrcmpiW( LPCWSTR str1, LPCWSTR str2 )
208 {
209     INT res;
210
211 #if 0
212     /* Too much!  (From registry loading.)  */
213     TRACE("strcmpi L%s and L%s\n",
214                    debugstr_w (str1), debugstr_w (str2));
215 #endif
216     if (!str1 || !str2) {
217         SetLastError(ERROR_INVALID_PARAMETER);
218         return 0;
219     }
220     while (*str1)
221     {
222         if ((*str1<0x100 ) && (*str2<0x100)) {
223             if ((res = toupper(*str1) - toupper(*str2)) != 0) return res;
224         } else {
225             if ((res = towupper(*str1) - towupper(*str2)) != 0) return res;
226         }
227         str1++;
228         str2++;
229     }
230     return towupper(*str1) - towupper(*str2);
231 }
232
233
234 /***********************************************************************
235  *           lstrcpy16   (KERNEL.88)
236  */
237 SEGPTR WINAPI lstrcpy16( SEGPTR dst, LPCSTR src )
238 {
239     /* this is how Windows does it */
240     memmove( (LPSTR)PTR_SEG_TO_LIN(dst), src, strlen(src)+1 );
241     return dst;
242 }
243
244
245 /***********************************************************************
246  *           lstrcpyA   (KERNEL32.608)
247  */
248 LPSTR WINAPI lstrcpyA( LPSTR dst, LPCSTR src )
249 {
250     __TRY
251     {
252         /* this is how Windows does it */
253         memmove( dst, src, strlen(src)+1 );
254     }
255     __EXCEPT(page_fault)
256     {
257         SetLastError( ERROR_INVALID_PARAMETER );
258         return NULL;
259     }
260     __ENDTRY
261     return dst;
262 }
263
264
265 /***********************************************************************
266  *           lstrcpyW   (KERNEL32.609)
267  */
268 LPWSTR WINAPI lstrcpyW( LPWSTR dst, LPCWSTR src )
269 {
270     __TRY
271     {
272         CRTDLL_wcscpy( dst, src );
273     }
274     __EXCEPT(page_fault)
275     {
276         SetLastError( ERROR_INVALID_PARAMETER );
277         return NULL;
278     }
279     __ENDTRY
280     return dst;
281 }
282
283
284 /***********************************************************************
285  *           lstrcpyn16   (KERNEL.353)
286  */
287 SEGPTR WINAPI lstrcpyn16( SEGPTR dst, LPCSTR src, INT16 n )
288 {
289     lstrcpynA( (LPSTR)PTR_SEG_TO_LIN(dst), src, n );
290     return dst;
291 }
292
293
294 /***********************************************************************
295  *           lstrcpyn32A   (KERNEL32.611)
296  * Note: this function differs from the UNIX strncpy, it _always_ writes
297  * a terminating \0
298  */
299 LPSTR WINAPI lstrcpynA( LPSTR dst, LPCSTR src, INT n )
300 {
301     LPSTR p = dst;
302     TRACE("strcpyn %s for %d chars\n",
303                    debugstr_an (src,n), n);
304     /* In real windows the whole function is protected by an exception handler
305      * that returns ERROR_INVALID_PARAMETER on faulty parameters
306      * We currently just check for NULL.
307      */
308     if (!dst || !src) {
309         SetLastError(ERROR_INVALID_PARAMETER);
310         return 0;
311     }
312     while ((n-- > 1) && *src) *p++ = *src++;
313     if (n >= 0) *p = 0;
314     return dst;
315 }
316
317
318 /***********************************************************************
319  *           lstrcpyn32W   (KERNEL32.612)
320  * Note: this function differs from the UNIX strncpy, it _always_ writes
321  * a terminating \0
322  */
323 LPWSTR WINAPI lstrcpynW( LPWSTR dst, LPCWSTR src, INT n )
324 {
325     LPWSTR p = dst;
326     TRACE("strcpyn L%s for %d chars\n",
327                    debugstr_wn (src,n), n);
328     /* In real windows the whole function is protected by an exception handler
329      * that returns ERROR_INVALID_PARAMETER on faulty parameters
330      * We currently just check for NULL.
331      */
332     if (!dst || !src) {
333         SetLastError(ERROR_INVALID_PARAMETER);
334         return 0;
335     }
336     while ((n-- > 1) && *src) *p++ = *src++;
337     if (n >= 0) *p = 0;
338     return dst;
339 }
340
341
342 /***********************************************************************
343  *           lstrlen16   (KERNEL.90)
344  */
345 INT16 WINAPI lstrlen16( LPCSTR str )
346 {
347     return (INT16)lstrlenA( str );
348 }
349
350
351 /***********************************************************************
352  *           lstrlenA   (KERNEL32.614)
353  */
354 INT WINAPI lstrlenA( LPCSTR str )
355 {
356     INT ret;
357     __TRY
358     {
359         ret = strlen(str);
360     }
361     __EXCEPT(page_fault)
362     {
363         SetLastError( ERROR_INVALID_PARAMETER );
364         return 0;
365     }
366     __ENDTRY
367     return ret;
368 }
369
370
371 /***********************************************************************
372  *           lstrlenW   (KERNEL32.615)
373  */
374 INT WINAPI lstrlenW( LPCWSTR str )
375 {
376     INT ret;
377     __TRY
378     {
379         ret = CRTDLL_wcslen(str);
380     }
381     __EXCEPT(page_fault)
382     {
383         SetLastError( ERROR_INVALID_PARAMETER );
384         return 0;
385     }
386     __ENDTRY
387     return ret;
388 }
389
390
391 /***********************************************************************
392  *           lstrcpyAtoW   (Not a Windows API)
393  */
394 LPWSTR WINAPI lstrcpyAtoW( LPWSTR dst, LPCSTR src )
395 {
396     register LPWSTR p = dst;
397
398     TRACE("%s\n",src);
399
400     while ((*p++ = (WCHAR)(unsigned char)*src++));
401     return dst;
402 }
403
404
405 /***********************************************************************
406  *           lstrcpyWtoA   (Not a Windows API)
407  */
408 LPSTR WINAPI lstrcpyWtoA( LPSTR dst, LPCWSTR src )
409 {
410     register LPSTR p = dst;
411
412     TRACE("L%s\n",debugstr_w(src));
413
414     while ((*p++ = (CHAR)*src++));
415     return dst;
416 }
417
418
419 /***********************************************************************
420  *           lstrcpynAtoW   (Not a Windows API)
421  * Note: this function differs from the UNIX strncpy, it _always_ writes
422  * a terminating \0
423  */
424 LPWSTR WINAPI lstrcpynAtoW( LPWSTR dst, LPCSTR src, INT n )
425 {
426     LPWSTR p = dst;
427
428     TRACE("%s %i\n",src, n);
429
430     while ((n-- > 1) && *src) *p++ = (WCHAR)(unsigned char)*src++;
431     if (n >= 0) *p = 0;
432     return dst;
433 }
434
435
436 /***********************************************************************
437  *           lstrcpynWtoA   (Not a Windows API)
438  * Note: this function differs from the UNIX strncpy, it _always_ writes
439  * a terminating \0
440  *
441  * The terminating zero should be written at the end of the string, not
442  * the end of the buffer, as some programs specify the wrong size for 
443  * the buffer (eg. winnt's sol.exe)
444  */
445 LPSTR WINAPI lstrcpynWtoA( LPSTR dst, LPCWSTR src, INT n )
446 {
447     if (--n >= 0)
448     {
449         n = CRTDLL_wcstombs( dst, src, n );
450         if(n<0)
451                  n=0;
452         dst[n] = 0;
453     }
454     return dst;
455 }
456
457 /***********************************************************************
458  *           UnicodeToAnsi   (KERNEL.434)
459  */
460 INT16 WINAPI UnicodeToAnsi16( LPCWSTR src, LPSTR dst, INT16 codepage )
461 {
462     if ( codepage != -1 )
463         FIXME("codepage %d not supported\n", codepage );
464
465     lstrcpyWtoA( dst, src );
466
467     return (INT16)lstrlenA( dst );
468 }
469
470
471 /***********************************************************************
472  *           Copy   (GDI.250)
473  */
474 void WINAPI Copy16( LPVOID src, LPVOID dst, WORD size )
475 {
476     memcpy( dst, src, size );
477 }
478
479
480 /***********************************************************************
481  *           RtlFillMemory   (KERNEL32.441)
482  */
483 VOID WINAPI RtlFillMemory( LPVOID ptr, UINT len, UINT fill )
484 {
485     memset( ptr, fill, len );
486 }
487
488
489 /***********************************************************************
490  *           RtlMoveMemory   (KERNEL32.442)
491  */
492 VOID WINAPI RtlMoveMemory( LPVOID dst, LPCVOID src, UINT len )
493 {
494     memmove( dst, src, len );
495 }
496
497
498 /***********************************************************************
499  *           RtlZeroMemory   (KERNEL32.444)
500  */
501 VOID WINAPI RtlZeroMemory( LPVOID ptr, UINT len )
502 {
503     memset( ptr, 0, len );
504 }
505
506
507 /***********************************************************************
508  *           AnsiToOem16   (KEYBOARD.5)
509  */
510 INT16 WINAPI AnsiToOem16( LPCSTR s, LPSTR d )
511 {
512     CharToOemA( s, d );
513     return -1;
514 }
515
516
517 /***********************************************************************
518  *           OemToAnsi16   (KEYBOARD.6)
519  */
520 INT16 WINAPI OemToAnsi16( LPCSTR s, LPSTR d )
521 {
522     OemToCharA( s, d );
523     return -1;
524 }
525
526
527 /***********************************************************************
528  *           AnsiToOemBuff16   (KEYBOARD.134)
529  */
530 void WINAPI AnsiToOemBuff16( LPCSTR s, LPSTR d, UINT16 len )
531 {
532     if (len != 0) CharToOemBuffA( s, d, len );
533 }
534
535
536 /***********************************************************************
537  *           OemToAnsiBuff16   (KEYBOARD.135)
538  */
539 void WINAPI OemToAnsiBuff16( LPCSTR s, LPSTR d, UINT16 len )
540 {
541     if (len != 0) OemToCharBuffA( s, d, len );
542 }
543
544
545 /***********************************************************************
546  *           CharToOem32A   (USER32.37)
547  */
548 BOOL WINAPI CharToOemA( LPCSTR s, LPSTR d )
549 {
550     LPSTR oldd = d;
551     if (!s || !d) return TRUE;
552     TRACE("CharToOem %s\n", debugstr_a (s));
553     while ((*d++ = ANSI_TO_OEM(*s++)));
554     TRACE("       to %s\n", debugstr_a (oldd));
555     return TRUE;
556 }
557
558
559 /***********************************************************************
560  *           CharToOemBuff32A   (USER32.38)
561  */
562 BOOL WINAPI CharToOemBuffA( LPCSTR s, LPSTR d, DWORD len )
563 {
564     while (len--) *d++ = ANSI_TO_OEM(*s++);
565     return TRUE;
566 }
567
568
569 /***********************************************************************
570  *           CharToOemBuff32W   (USER32.39)
571  */
572 BOOL WINAPI CharToOemBuffW( LPCWSTR s, LPSTR d, DWORD len )
573 {
574     while (len--) *d++ = ANSI_TO_OEM(*s++);
575     return TRUE;
576 }
577
578
579 /***********************************************************************
580  *           CharToOem32W   (USER32.40)
581  */
582 BOOL WINAPI CharToOemW( LPCWSTR s, LPSTR d )
583 {
584     LPSTR oldd = d;
585     if (!s || !d) return TRUE;
586     TRACE("CharToOem L%s\n", debugstr_w (s));
587     while ((*d++ = ANSI_TO_OEM(*s++)));
588     TRACE("       to %s\n", debugstr_a (oldd));
589     return TRUE;
590 }
591
592
593 /***********************************************************************
594  *           OemToChar32A   (USER32.402)
595  */
596 BOOL WINAPI OemToCharA( LPCSTR s, LPSTR d )
597 {
598     LPSTR oldd = d;
599     TRACE("OemToChar %s\n", debugstr_a (s));
600     while ((*d++ = OEM_TO_ANSI(*s++)));
601     TRACE("       to %s\n", debugstr_a (oldd));
602     return TRUE;
603 }
604
605
606 /***********************************************************************
607  *           OemToCharBuff32A   (USER32.403)
608  */
609 BOOL WINAPI OemToCharBuffA( LPCSTR s, LPSTR d, DWORD len )
610 {
611     TRACE("OemToCharBuff %s\n", debugstr_an (s, len));
612     while (len--) *d++ = OEM_TO_ANSI(*s++);
613     return TRUE;
614 }
615
616
617 /***********************************************************************
618  *           OemToCharBuff32W   (USER32.404)
619  */
620 BOOL WINAPI OemToCharBuffW( LPCSTR s, LPWSTR d, DWORD len )
621 {
622     TRACE("OemToCharBuff %s\n", debugstr_an (s, len));
623     while (len--) *d++ = (WCHAR)OEM_TO_ANSI(*s++);
624     return TRUE;
625 }
626
627
628 /***********************************************************************
629  *           OemToChar32W   (USER32.405)
630  */
631 BOOL WINAPI OemToCharW( LPCSTR s, LPWSTR d )
632 {
633     while ((*d++ = (WCHAR)OEM_TO_ANSI(*s++)));
634     return TRUE;
635 }
636
637 /***********************************************************************
638  *  WideCharToLocal (Not a Windows API)
639  *  similar lstrcpyWtoA, should handle codepages properly
640  *
641  *  RETURNS
642  *    strlen of the destination string
643  */
644  
645 INT WINAPI WideCharToLocal(
646     LPSTR pLocal, 
647                 LPCWSTR pWide, 
648                 INT dwChars)
649 { *pLocal = 0;
650   TRACE("(%p, %s, %i)\n",       pLocal, debugstr_w(pWide),dwChars);
651   WideCharToMultiByte(CP_ACP,0,pWide,-1,pLocal,dwChars,NULL,NULL);
652   return strlen(pLocal);
653 }
654 /***********************************************************************
655  *  LocalToWideChar (Not a Windows API)
656  *  similar lstrcpyAtoW, should handle codepages properly
657  *
658  *  RETURNS
659  *    strlen of the destination string
660  */
661 INT WINAPI LocalToWideChar(
662     LPWSTR pWide, 
663                 LPCSTR pLocal, 
664                 INT dwChars)
665 { *pWide = 0;
666   TRACE("(%p, %s, %i)\n",pWide, pLocal, dwChars);
667         MultiByteToWideChar(CP_ACP,0,pLocal,-1,pWide,dwChars); 
668   return lstrlenW(pWide);
669 }
670
671
672 /***********************************************************************
673  *           lstrrchr   (Not a Windows API)
674  *
675  * This is the implementation meant to be invoked form within
676  * COMCTL32_StrRChrA and shell32(TODO)...
677  *
678  * Return a pointer to the last occurence of wMatch in lpStart
679  * not looking further than lpEnd...
680  */
681 LPSTR WINAPI lstrrchr( LPCSTR lpStart, LPCSTR lpEnd, WORD wMatch )
682 {
683   LPCSTR lpGotIt = NULL;
684
685   TRACE("(%s, %s)\n", lpStart, lpEnd);
686
687   if (!lpEnd) lpEnd = lpStart + strlen(lpStart);
688
689   for(; lpStart < lpEnd; lpStart = CharNextA(lpStart)) 
690     if (!ChrCmpA( GET_WORD(lpStart), wMatch)) 
691       lpGotIt = lpStart;
692     
693   return ((LPSTR)lpGotIt);
694 }
695
696 /***********************************************************************
697  *           ChrCmpW   
698  * This fuction returns FALSE if both words match, TRUE otherwise...
699  */
700 static BOOL ChrCmpW( WORD word1, WORD word2) {
701   return (word1 != word2);
702 }
703
704 /***********************************************************************
705  *           lstrrchrw    (Not a Windows API)
706  *
707  * This is the implementation meant to be invoked form within
708  * COMCTL32_StrRChrW and shell32(TODO)...
709  *
710  * Return a pointer to the last occurence of wMatch in lpStart
711  * not looking further than lpEnd...
712  */  
713 LPWSTR WINAPI lstrrchrw( LPCWSTR lpStart, LPCWSTR lpEnd, WORD wMatch )
714 {
715   LPCWSTR lpGotIt = NULL;
716
717   TRACE("(%p, %p, %c)\n", lpStart,      lpEnd, wMatch);
718   if (!lpEnd) lpEnd = lpStart + lstrlenW(lpStart);
719
720   for(; lpStart < lpEnd; lpStart = CharNextW(lpStart)) 
721     if (!ChrCmpW( GET_WORD(lpStart), wMatch)) 
722       lpGotIt = lpStart;
723     
724   return (LPWSTR)lpGotIt;
725 }
726
727 /***********************************************************************
728  *           ChrCmpA   
729  * This fuction returns FALSE if both words match, TRUE otherwise...
730  */
731 static BOOL ChrCmpA( WORD word1, WORD word2) {
732   if (LOBYTE(word1) == LOBYTE(word2)) {
733     if (IsDBCSLeadByte(LOBYTE(word1))) {
734       return (word1 != word2);
735     }
736     return FALSE;
737   }
738   return TRUE;
739 }