windows-unicode-support-20080509
[openafs.git] / src / WINNT / afsd / cm_utils.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <errno.h>
14 #include <windows.h>
15 #include <winsock2.h>
16 #ifndef EWOULDBLOCK
17 #define EWOULDBLOCK             WSAEWOULDBLOCK
18 #define EINPROGRESS             WSAEINPROGRESS
19 #define EALREADY                WSAEALREADY
20 #define ENOTSOCK                WSAENOTSOCK
21 #define EDESTADDRREQ            WSAEDESTADDRREQ
22 #define EMSGSIZE                WSAEMSGSIZE
23 #define EPROTOTYPE              WSAEPROTOTYPE
24 #define ENOPROTOOPT             WSAENOPROTOOPT
25 #define EPROTONOSUPPORT         WSAEPROTONOSUPPORT
26 #define ESOCKTNOSUPPORT         WSAESOCKTNOSUPPORT
27 #define EOPNOTSUPP              WSAEOPNOTSUPP
28 #define EPFNOSUPPORT            WSAEPFNOSUPPORT
29 #define EAFNOSUPPORT            WSAEAFNOSUPPORT
30 #define EADDRINUSE              WSAEADDRINUSE
31 #define EADDRNOTAVAIL           WSAEADDRNOTAVAIL
32 #define ENETDOWN                WSAENETDOWN
33 #define ENETUNREACH             WSAENETUNREACH
34 #define ENETRESET               WSAENETRESET
35 #define ECONNABORTED            WSAECONNABORTED
36 #define ECONNRESET              WSAECONNRESET
37 #define ENOBUFS                 WSAENOBUFS
38 #define EISCONN                 WSAEISCONN
39 #define ENOTCONN                WSAENOTCONN
40 #define ESHUTDOWN               WSAESHUTDOWN
41 #define ETOOMANYREFS            WSAETOOMANYREFS
42 #define ETIMEDOUT               WSAETIMEDOUT
43 #define ECONNREFUSED            WSAECONNREFUSED
44 #ifdef ELOOP
45 #undef ELOOP
46 #endif
47 #define ELOOP                   WSAELOOP
48 #ifdef ENAMETOOLONG
49 #undef ENAMETOOLONG
50 #endif
51 #define ENAMETOOLONG            WSAENAMETOOLONG
52 #define EHOSTDOWN               WSAEHOSTDOWN
53 #define EHOSTUNREACH            WSAEHOSTUNREACH
54 #ifdef ENOTEMPTY
55 #undef ENOTEMPTY
56 #endif 
57 #define ENOTEMPTY               WSAENOTEMPTY
58 #define EPROCLIM                WSAEPROCLIM
59 #define EUSERS                  WSAEUSERS
60 #define EDQUOT                  WSAEDQUOT
61 #define ESTALE                  WSAESTALE
62 #define EREMOTE                 WSAEREMOTE
63 #endif /* EWOULDBLOCK */
64 #include <afs/unified_afs.h>
65
66 #include <string.h>
67 #include <malloc.h>
68 #include "afsd.h"
69 #include <osi.h>
70 #include <rx/rx.h>
71
72 #define STRSAFE_NO_DEPRECATE
73 #include <strsafe.h>
74
75
76 static osi_once_t cm_utilsOnce;
77
78 osi_rwlock_t cm_utilsLock;
79
80 cm_space_t *cm_spaceListp;
81
82 static int et2sys[512];
83
84 void
85 init_et_to_sys_error(void)
86 {
87     memset(&et2sys, 0, sizeof(et2sys));
88     et2sys[(UAEPERM - ERROR_TABLE_BASE_uae)] = EPERM;
89     et2sys[(UAENOENT - ERROR_TABLE_BASE_uae)] = ENOENT;
90     et2sys[(UAESRCH - ERROR_TABLE_BASE_uae)] = ESRCH;
91     et2sys[(UAEINTR - ERROR_TABLE_BASE_uae)] = EINTR;
92     et2sys[(UAEIO - ERROR_TABLE_BASE_uae)] = EIO;
93     et2sys[(UAENXIO - ERROR_TABLE_BASE_uae)] = ENXIO;
94     et2sys[(UAE2BIG - ERROR_TABLE_BASE_uae)] = E2BIG;
95     et2sys[(UAENOEXEC - ERROR_TABLE_BASE_uae)] = ENOEXEC;
96     et2sys[(UAEBADF - ERROR_TABLE_BASE_uae)] = EBADF;
97     et2sys[(UAECHILD - ERROR_TABLE_BASE_uae)] = ECHILD;
98     et2sys[(UAEAGAIN - ERROR_TABLE_BASE_uae)] = EAGAIN;
99     et2sys[(UAENOMEM - ERROR_TABLE_BASE_uae)] = ENOMEM;
100     et2sys[(UAEACCES - ERROR_TABLE_BASE_uae)] = EACCES;
101     et2sys[(UAEFAULT - ERROR_TABLE_BASE_uae)] = EFAULT;
102     et2sys[(UAENOTBLK - ERROR_TABLE_BASE_uae)] = ENOTBLK;
103     et2sys[(UAEBUSY - ERROR_TABLE_BASE_uae)] = EBUSY;
104     et2sys[(UAEEXIST - ERROR_TABLE_BASE_uae)] = EEXIST;
105     et2sys[(UAEXDEV - ERROR_TABLE_BASE_uae)] = EXDEV;
106     et2sys[(UAENODEV - ERROR_TABLE_BASE_uae)] = ENODEV;
107     et2sys[(UAENOTDIR - ERROR_TABLE_BASE_uae)] = ENOTDIR;
108     et2sys[(UAEISDIR - ERROR_TABLE_BASE_uae)] = EISDIR;
109     et2sys[(UAEINVAL - ERROR_TABLE_BASE_uae)] = EINVAL;
110     et2sys[(UAENFILE - ERROR_TABLE_BASE_uae)] = ENFILE;
111     et2sys[(UAEMFILE - ERROR_TABLE_BASE_uae)] = EMFILE;
112     et2sys[(UAENOTTY - ERROR_TABLE_BASE_uae)] = ENOTTY;
113     et2sys[(UAETXTBSY - ERROR_TABLE_BASE_uae)] = ETXTBSY;
114     et2sys[(UAEFBIG - ERROR_TABLE_BASE_uae)] = EFBIG;
115     et2sys[(UAENOSPC - ERROR_TABLE_BASE_uae)] = ENOSPC;
116     et2sys[(UAESPIPE - ERROR_TABLE_BASE_uae)] = ESPIPE;
117     et2sys[(UAEROFS - ERROR_TABLE_BASE_uae)] = EROFS;
118     et2sys[(UAEMLINK - ERROR_TABLE_BASE_uae)] = EMLINK;
119     et2sys[(UAEPIPE - ERROR_TABLE_BASE_uae)] = EPIPE;
120     et2sys[(UAEDOM - ERROR_TABLE_BASE_uae)] = EDOM;
121     et2sys[(UAERANGE - ERROR_TABLE_BASE_uae)] = ERANGE;
122     et2sys[(UAEDEADLK - ERROR_TABLE_BASE_uae)] = EDEADLK;
123     et2sys[(UAENAMETOOLONG - ERROR_TABLE_BASE_uae)] = ENAMETOOLONG;
124     et2sys[(UAENOLCK - ERROR_TABLE_BASE_uae)] = ENOLCK;
125     et2sys[(UAENOSYS - ERROR_TABLE_BASE_uae)] = ENOSYS;
126     et2sys[(UAENOTEMPTY - ERROR_TABLE_BASE_uae)] = ENOTEMPTY;
127     et2sys[(UAELOOP - ERROR_TABLE_BASE_uae)] = ELOOP;
128     et2sys[(UAEWOULDBLOCK - ERROR_TABLE_BASE_uae)] = EWOULDBLOCK;
129     et2sys[(UAENOMSG - ERROR_TABLE_BASE_uae)] = ENOMSG;
130     et2sys[(UAEIDRM - ERROR_TABLE_BASE_uae)] = EIDRM;
131     et2sys[(UAECHRNG - ERROR_TABLE_BASE_uae)] = ECHRNG;
132     et2sys[(UAEL2NSYNC - ERROR_TABLE_BASE_uae)] = EL2NSYNC;
133     et2sys[(UAEL3HLT - ERROR_TABLE_BASE_uae)] = EL3HLT;
134     et2sys[(UAEL3RST - ERROR_TABLE_BASE_uae)] = EL3RST;
135     et2sys[(UAELNRNG - ERROR_TABLE_BASE_uae)] = ELNRNG;
136     et2sys[(UAEUNATCH - ERROR_TABLE_BASE_uae)] = EUNATCH;
137     et2sys[(UAENOCSI - ERROR_TABLE_BASE_uae)] = ENOCSI;
138     et2sys[(UAEL2HLT - ERROR_TABLE_BASE_uae)] = EL2HLT;
139     et2sys[(UAEBADE - ERROR_TABLE_BASE_uae)] = EBADE;
140     et2sys[(UAEBADR - ERROR_TABLE_BASE_uae)] = EBADR;
141     et2sys[(UAEXFULL - ERROR_TABLE_BASE_uae)] = EXFULL;
142     et2sys[(UAENOANO - ERROR_TABLE_BASE_uae)] = ENOANO;
143     et2sys[(UAEBADRQC - ERROR_TABLE_BASE_uae)] = EBADRQC;
144     et2sys[(UAEBADSLT - ERROR_TABLE_BASE_uae)] = EBADSLT;
145     et2sys[(UAEBFONT - ERROR_TABLE_BASE_uae)] = EBFONT;
146     et2sys[(UAENOSTR - ERROR_TABLE_BASE_uae)] = ENOSTR;
147     et2sys[(UAENODATA - ERROR_TABLE_BASE_uae)] = ENODATA;
148     et2sys[(UAETIME - ERROR_TABLE_BASE_uae)] = ETIME;
149     et2sys[(UAENOSR - ERROR_TABLE_BASE_uae)] = ENOSR;
150     et2sys[(UAENONET - ERROR_TABLE_BASE_uae)] = ENONET;
151     et2sys[(UAENOPKG - ERROR_TABLE_BASE_uae)] = ENOPKG;
152     et2sys[(UAEREMOTE - ERROR_TABLE_BASE_uae)] = EREMOTE;
153     et2sys[(UAENOLINK - ERROR_TABLE_BASE_uae)] = ENOLINK;
154     et2sys[(UAEADV - ERROR_TABLE_BASE_uae)] = EADV;
155     et2sys[(UAESRMNT - ERROR_TABLE_BASE_uae)] = ESRMNT;
156     et2sys[(UAECOMM - ERROR_TABLE_BASE_uae)] = ECOMM;
157     et2sys[(UAEPROTO - ERROR_TABLE_BASE_uae)] = EPROTO;
158     et2sys[(UAEMULTIHOP - ERROR_TABLE_BASE_uae)] = EMULTIHOP;
159     et2sys[(UAEDOTDOT - ERROR_TABLE_BASE_uae)] = EDOTDOT;
160     et2sys[(UAEBADMSG - ERROR_TABLE_BASE_uae)] = EBADMSG;
161     et2sys[(UAEOVERFLOW - ERROR_TABLE_BASE_uae)] = EOVERFLOW;
162     et2sys[(UAENOTUNIQ - ERROR_TABLE_BASE_uae)] = ENOTUNIQ;
163     et2sys[(UAEBADFD - ERROR_TABLE_BASE_uae)] = EBADFD;
164     et2sys[(UAEREMCHG - ERROR_TABLE_BASE_uae)] = EREMCHG;
165     et2sys[(UAELIBACC - ERROR_TABLE_BASE_uae)] = ELIBACC;
166     et2sys[(UAELIBBAD - ERROR_TABLE_BASE_uae)] = ELIBBAD;
167     et2sys[(UAELIBSCN - ERROR_TABLE_BASE_uae)] = ELIBSCN;
168     et2sys[(UAELIBMAX - ERROR_TABLE_BASE_uae)] = ELIBMAX;
169     et2sys[(UAELIBEXEC - ERROR_TABLE_BASE_uae)] = ELIBEXEC;
170     et2sys[(UAEILSEQ - ERROR_TABLE_BASE_uae)] = EILSEQ;
171     et2sys[(UAERESTART - ERROR_TABLE_BASE_uae)] = ERESTART;
172     et2sys[(UAESTRPIPE - ERROR_TABLE_BASE_uae)] = ESTRPIPE;
173     et2sys[(UAEUSERS - ERROR_TABLE_BASE_uae)] = EUSERS;
174     et2sys[(UAENOTSOCK - ERROR_TABLE_BASE_uae)] = ENOTSOCK;
175     et2sys[(UAEDESTADDRREQ - ERROR_TABLE_BASE_uae)] = EDESTADDRREQ;
176     et2sys[(UAEMSGSIZE - ERROR_TABLE_BASE_uae)] = EMSGSIZE;
177     et2sys[(UAEPROTOTYPE - ERROR_TABLE_BASE_uae)] = EPROTOTYPE;
178     et2sys[(UAENOPROTOOPT - ERROR_TABLE_BASE_uae)] = ENOPROTOOPT;
179     et2sys[(UAEPROTONOSUPPORT - ERROR_TABLE_BASE_uae)] = EPROTONOSUPPORT;
180     et2sys[(UAESOCKTNOSUPPORT - ERROR_TABLE_BASE_uae)] = ESOCKTNOSUPPORT;
181     et2sys[(UAEOPNOTSUPP - ERROR_TABLE_BASE_uae)] = EOPNOTSUPP;
182     et2sys[(UAEPFNOSUPPORT - ERROR_TABLE_BASE_uae)] = EPFNOSUPPORT;
183     et2sys[(UAEAFNOSUPPORT - ERROR_TABLE_BASE_uae)] = EAFNOSUPPORT;
184     et2sys[(UAEADDRINUSE - ERROR_TABLE_BASE_uae)] = EADDRINUSE;
185     et2sys[(UAEADDRNOTAVAIL - ERROR_TABLE_BASE_uae)] = EADDRNOTAVAIL;
186     et2sys[(UAENETDOWN - ERROR_TABLE_BASE_uae)] = ENETDOWN;
187     et2sys[(UAENETUNREACH - ERROR_TABLE_BASE_uae)] = ENETUNREACH;
188     et2sys[(UAENETRESET - ERROR_TABLE_BASE_uae)] = ENETRESET;
189     et2sys[(UAECONNABORTED - ERROR_TABLE_BASE_uae)] = ECONNABORTED;
190     et2sys[(UAECONNRESET - ERROR_TABLE_BASE_uae)] = ECONNRESET;
191     et2sys[(UAENOBUFS - ERROR_TABLE_BASE_uae)] = ENOBUFS;
192     et2sys[(UAEISCONN - ERROR_TABLE_BASE_uae)] = EISCONN;
193     et2sys[(UAENOTCONN - ERROR_TABLE_BASE_uae)] = ENOTCONN;
194     et2sys[(UAESHUTDOWN - ERROR_TABLE_BASE_uae)] = ESHUTDOWN;
195     et2sys[(UAETOOMANYREFS - ERROR_TABLE_BASE_uae)] = ETOOMANYREFS;
196     et2sys[(UAETIMEDOUT - ERROR_TABLE_BASE_uae)] = ETIMEDOUT;
197     et2sys[(UAECONNREFUSED - ERROR_TABLE_BASE_uae)] = ECONNREFUSED;
198     et2sys[(UAEHOSTDOWN - ERROR_TABLE_BASE_uae)] = EHOSTDOWN;
199     et2sys[(UAEHOSTUNREACH - ERROR_TABLE_BASE_uae)] = EHOSTUNREACH;
200     et2sys[(UAEALREADY - ERROR_TABLE_BASE_uae)] = EALREADY;
201     et2sys[(UAEINPROGRESS - ERROR_TABLE_BASE_uae)] = EINPROGRESS;
202     et2sys[(UAESTALE - ERROR_TABLE_BASE_uae)] = ESTALE;
203     et2sys[(UAEUCLEAN - ERROR_TABLE_BASE_uae)] = EUCLEAN;
204     et2sys[(UAENOTNAM - ERROR_TABLE_BASE_uae)] = ENOTNAM;
205     et2sys[(UAENAVAIL - ERROR_TABLE_BASE_uae)] = ENAVAIL;
206     et2sys[(UAEISNAM - ERROR_TABLE_BASE_uae)] = EISNAM;
207     et2sys[(UAEREMOTEIO - ERROR_TABLE_BASE_uae)] = EREMOTEIO;
208     et2sys[(UAEDQUOT - ERROR_TABLE_BASE_uae)] = EDQUOT;
209     et2sys[(UAENOMEDIUM - ERROR_TABLE_BASE_uae)] = ENOMEDIUM;
210     et2sys[(UAEMEDIUMTYPE - ERROR_TABLE_BASE_uae)] = EMEDIUMTYPE;
211 }
212
213 static afs_int32
214 et_to_sys_error(afs_int32 in)
215 {
216     if (in < ERROR_TABLE_BASE_uae || in >= ERROR_TABLE_BASE_uae + 512)
217         return in;
218     if (et2sys[in - ERROR_TABLE_BASE_uae] != 0)
219         return et2sys[in - ERROR_TABLE_BASE_uae];
220     return in;
221 }
222
223 long cm_MapRPCError(long error, cm_req_t *reqp)
224 {
225     if (error == 0) 
226         return 0;
227
228     /* If we had to stop retrying, report our saved error code. */
229     if (reqp && error == CM_ERROR_TIMEDOUT) {
230         if (reqp->accessError)
231             return reqp->accessError;
232         if (reqp->volumeError)
233             return reqp->volumeError;
234         if (reqp->rpcError)
235             return reqp->rpcError;
236         return error;
237     }
238
239     error = et_to_sys_error(error);
240
241     if (error < 0) 
242         error = CM_ERROR_TIMEDOUT;
243     else if (error == EROFS) 
244         error = CM_ERROR_READONLY;
245     else if (error == EACCES) 
246         error = CM_ERROR_NOACCESS;
247     else if (error == EXDEV) 
248         error = CM_ERROR_CROSSDEVLINK;
249     else if (error == EEXIST) 
250         error = CM_ERROR_EXISTS;
251     else if (error == ENOTDIR) 
252         error = CM_ERROR_NOTDIR;
253     else if (error == ENOENT)
254         error = CM_ERROR_NOSUCHFILE;
255     else if (error == EAGAIN
256              || error == 35        /* EAGAIN, Digital UNIX */
257              || error == WSAEWOULDBLOCK)
258         error = CM_ERROR_WOULDBLOCK;
259     else if (error == VDISKFULL
260               || error == ENOSPC)
261         error = CM_ERROR_SPACE;
262     else if (error == VOVERQUOTA
263               || error == EDQUOT
264               || error == 49    /* EDQUOT on Solaris */
265               || error == 88    /* EDQUOT on AIX */
266               || error == 69    /* EDQUOT on Digital UNIX and HPUX */
267               || error == 122   /* EDQUOT on Linux */
268               || error == 1133) /* EDQUOT on Irix  */
269         error = CM_ERROR_QUOTA;
270     else if (error == VNOVNODE)
271         error = CM_ERROR_BADFD;
272     else if (error == EISDIR)
273         return CM_ERROR_ISDIR;
274     return error;
275 }
276
277 long cm_MapRPCErrorRmdir(long error, cm_req_t *reqp)
278 {
279     if (error == 0) 
280         return 0;
281
282     /* If we had to stop retrying, report our saved error code. */
283     if (reqp && error == CM_ERROR_TIMEDOUT) {
284         if (reqp->accessError)
285             return reqp->accessError;
286         if (reqp->volumeError)
287             return reqp->volumeError;
288         if (reqp->rpcError)
289             return reqp->rpcError;
290         return error;
291     }
292
293     error = et_to_sys_error(error);
294
295     if (error < 0) 
296         error = CM_ERROR_TIMEDOUT;
297     else if (error == EROFS) 
298         error = CM_ERROR_READONLY;
299     else if (error == ENOTDIR) 
300         error = CM_ERROR_NOTDIR;
301     else if (error == EACCES) 
302         error = CM_ERROR_NOACCESS;
303     else if (error == ENOENT) 
304         error = CM_ERROR_NOSUCHFILE;
305     else if (error == ENOTEMPTY 
306               || error == 17            /* AIX */
307               || error == 66            /* SunOS 4, Digital UNIX */
308               || error == 93            /* Solaris 2, IRIX */
309               || error == 247)  /* HP/UX */
310         error = CM_ERROR_NOTEMPTY;
311     return error;
312 }       
313
314 long cm_MapVLRPCError(long error, cm_req_t *reqp)
315 {
316     if (error == 0) return 0;
317
318     /* If we had to stop retrying, report our saved error code. */
319     if (reqp && error == CM_ERROR_TIMEDOUT) {
320         if (reqp->accessError)
321             return reqp->accessError;
322         if (reqp->volumeError)
323             return reqp->volumeError;
324         if (reqp->rpcError)
325             return reqp->rpcError;
326         return error;
327     }
328
329     error = et_to_sys_error(error);
330
331     if (error < 0) 
332         error = CM_ERROR_TIMEDOUT;
333     else if (error == VL_NOENT) 
334         error = CM_ERROR_NOSUCHVOLUME;
335     return error;
336 }
337
338 cm_space_t *cm_GetSpace(void)
339 {
340         cm_space_t *tsp;
341
342         if (osi_Once(&cm_utilsOnce)) {
343                 lock_InitializeRWLock(&cm_utilsLock, "cm_utilsLock");
344                 osi_EndOnce(&cm_utilsOnce);
345         }
346         
347         lock_ObtainWrite(&cm_utilsLock);
348         if (tsp = cm_spaceListp) {
349                 cm_spaceListp = tsp->nextp;
350         }
351         else tsp = (cm_space_t *) malloc(sizeof(cm_space_t));
352         (void) memset(tsp, 0, sizeof(cm_space_t));
353         lock_ReleaseWrite(&cm_utilsLock);
354         
355         return tsp;
356 }
357
358 void cm_FreeSpace(cm_space_t *tsp)
359 {
360         lock_ObtainWrite(&cm_utilsLock);
361         tsp->nextp = cm_spaceListp;
362         cm_spaceListp = tsp;
363         lock_ReleaseWrite(&cm_utilsLock);
364 }
365
366 /* This is part of the Microsoft Internationalized Domain Name
367    Mitigation APIs. */
368 #include <normalization.h>
369
370 int
371 (WINAPI *pNormalizeString)( __in NORM_FORM NormForm,
372                             __in_ecount(cwSrcLength) LPCWSTR lpSrcString,
373                             __in int cwSrcLength,
374                             __out_ecount(cwDstLength) LPWSTR lpDstString,
375                             __in int cwDstLength ) = NULL;
376
377 BOOL
378 (WINAPI *pIsNormalizedString)( __in NORM_FORM NormForm,
379                                __in_ecount(cwLength) LPCWSTR lpString,
380                                __in int cwLength ) = NULL;
381
382
383 #define NLSDLLNAME "Normaliz.dll"
384 #define NLSMAXCCH  1024
385 #define NLSERRCCH  8
386
387 #define AFS_NORM_FORM NormalizationC
388
389 long cm_InitNormalization(void)
390 {
391     HMODULE h_Nls;
392
393     if (pNormalizeString != NULL)
394         return 0;
395
396     h_Nls = LoadLibrary(NLSDLLNAME);
397     if (h_Nls == INVALID_HANDLE_VALUE) {
398         afsi_log("Can't load " NLSDLLNAME ": LastError=%d", GetLastError());
399         return 1;
400     }
401
402     pNormalizeString = GetProcAddress(h_Nls, "NormalizeString");
403     pIsNormalizedString = GetProcAddress(h_Nls, "IsNormalizedString");
404
405     return (pNormalizeString && pIsNormalizedString);
406 }
407
408 /* \brief Normalize a UTF-16 string.
409
410    If the supplied destination buffer is
411    insufficient or NULL, then a new buffer will be allocated to hold
412    the normalized string.
413
414    \param[in] src : Source UTF-16 string.  Length is specified in
415        cch_src.
416
417    \param[in] cch_src : The character count in cch_src is assumed to
418        be tight and include the terminating NULL character if there is
419        one.  If the NULL is absent, the resulting string will not be
420        NULL terminated.
421
422    \param[out] ext_dest : The destination buffer.  Can be NULL, in
423        which case *pcch_dest MUST be NULL.
424
425    \param[in,out] pcch_dest : On entry *pcch_dest contains a count of
426        characters in the destination buffer.  On exit, it will contain
427        a count of characters that were copied to the destination
428        buffer.
429
430    Returns a pointer to the buffer containing the normalized string or
431    NULL if the call was unsuccessful.  If the returned destination
432    buffer is different fron the supplied buffer and non-NULL, it
433    should be freed using free().
434 */
435 static wchar_t * 
436 NormalizeUtf16String(const wchar_t * src, int cch_src, wchar_t * ext_dest, int *pcch_dest)
437 {
438     if ((pIsNormalizedString && (*pIsNormalizedString)(AFS_NORM_FORM, src, cch_src)) ||
439         (!pNormalizeString)) {
440
441         int rv;
442         DWORD gle;
443         int tries = 10;
444         wchar_t * dest;
445         int cch_dest = *pcch_dest;
446
447         dest = ext_dest;
448
449         while (tries-- > 0) {
450
451             rv = (*pNormalizeString)(AFS_NORM_FORM, src, cch_src, dest, cch_dest);
452
453             if (rv <= 0 && (gle = GetLastError()) != ERROR_SUCCESS) {
454 #ifdef DEBUG
455                 osi_Log1(afsd_logp, "NormalizeUtf16String error = %d", gle);
456 #endif
457                 if (gle == ERROR_INSUFFICIENT_BUFFER) {
458
459                     /* The buffer wasn't big enough.  We are going to
460                        try allocating one. */
461
462                     cch_dest = (-rv) + NLSERRCCH;
463                     goto cont;
464
465                 } else {
466                     /* Something else is wrong */
467                     break;
468                 }
469
470             } else if (rv < 0) { /* rv < 0 && gle == ERROR_SUCCESS */
471
472                 /* Technically not one of the expected outcomes */
473                 break;
474
475             } else {            /* rv > 0 || (rv == 0 && gle == ERROR_SUCCESS) */
476
477                 /* Possibly succeeded */
478
479                 if (rv == 0) { /* Succeeded and the return string is empty */
480                     *pcch_dest = 0;
481                     return dest;
482                 }
483
484                 if (cch_dest == 0) {
485                     /* Nope.  We only calculated the required size of the buffer */
486
487                     cch_dest = rv + NLSERRCCH;
488                     goto cont;
489                 }
490
491                 *pcch_dest = rv;
492
493                 /* Success! */
494                 return dest;
495             }
496
497         cont:
498             if (dest != ext_dest && dest)
499                 free(dest);
500             dest = malloc(cch_dest * sizeof(wchar_t));
501         }
502
503         /* Failed */
504
505         if (dest != ext_dest && dest)
506             free(dest);
507
508         *pcch_dest = 0;
509         return NULL;
510     } else {
511
512         /* No need to or unable to normalize.  Just copy the string */
513         if (SUCCEEDED(StringCchCopyNW(ext_dest, *pcch_dest, src, cch_src))) {
514             *pcch_dest = cch_src;
515             return ext_dest;
516         } else {
517             *pcch_dest = 0;
518             return NULL;
519         }
520     }
521 }
522
523 /* \brief Normalize a UTF-16 string into a UTF-8 string.
524
525    \param[in] src : Source string.
526
527    \param[in] cch_src : Count of characters in src. If the count includes the
528        NULL terminator, then the resulting string will be NULL
529        terminated.  If it is -1, then src is assumed to be NULL
530        terminated.
531
532    \param[out] adest : Destination buffer.
533
534    \param[in] cch_adest : Number of characters in the destination buffer.
535
536    Returns the number of characters stored into cch_adest. This will
537    include the terminating NULL if cch_src included the terminating
538    NULL or was -1.  If this is 0, then the operation was unsuccessful.
539  */
540 long cm_NormalizeUtf16StringToUtf8(const wchar_t * src, int cch_src,
541                                    char * adest, int cch_adest)
542 {
543     if (cch_src < 0) {
544         size_t cch;
545
546         if (FAILED(StringCchLengthW(src, NLSMAXCCH, &cch)))
547             return CM_ERROR_TOOBIG;
548
549         cch_src = cch+1;
550     }
551
552     {
553         wchar_t nbuf[NLSMAXCCH];
554         wchar_t * normalized;
555         int cch_norm = NLSMAXCCH;
556
557         normalized = NormalizeUtf16String(src, cch_src, nbuf, &cch_norm);
558         if (normalized) {
559             cch_adest = WideCharToMultiByte(CP_UTF8, 0, normalized, cch_norm,
560                                             adest, cch_adest, NULL, 0);
561
562             if (normalized != nbuf && normalized)
563                 free(normalized);
564
565             return cch_adest;
566
567         } else {
568
569             return 0;
570
571         }
572     }
573 }
574
575
576 /* \brief Normalize a UTF-8 string.
577
578    \param[in] src String to normalize.
579
580    \param[in] cch_src : Count of characters in src.  If this value is
581        -1, then src is assumed to be NULL terminated.  The translated
582        string will be NULL terminated only if this is -1 or the count
583        includes the terminating NULL.
584
585    \param[out] adest : Destination string.
586
587    \param[in] cch_adest : Number of characters in the destination
588        string.
589
590    Returns the number of characters stored into adest or 0 if the call
591    was unsuccessful.
592  */
593 long cm_NormalizeUtf8String(const char * src, int cch_src,
594                             char * adest, int cch_adest)
595 {
596     wchar_t wsrcbuf[NLSMAXCCH];
597     wchar_t *wnorm;
598     int cch;
599     int cch_norm;
600
601     /* Get some edge cases out first, so we don't have to worry about
602        cch_src being 0 etc. */
603     if (cch_src == 0) {
604         return 0;
605     } else if (*src == '\0') {
606         *adest = '\0';
607         return 1;
608     }
609
610     cch = MultiByteToWideChar(CP_UTF8, 0, src, cch_src * sizeof(char),
611                              wsrcbuf, NLSMAXCCH);
612
613     if (cch == 0) {
614 #ifdef DEBUG
615         DebugBreak();
616 #endif
617         return 0;
618     }
619
620     cch_norm = 0;
621     wnorm = NormalizeUtf16String(wsrcbuf, cch, NULL, &cch_norm);
622     if (wnorm == NULL) {
623 #ifdef DEBUG
624         DebugBreak();
625 #endif
626         return 0;
627     }
628
629     cch = WideCharToMultiByte(CP_UTF8, 0, wnorm, cch_norm,
630                               adest, cch_adest * sizeof(char),
631                               NULL, FALSE);
632
633     if (wnorm)
634         free(wnorm);
635
636     return cch;
637 }